Angular 7. Как заставить вызовы API ждать завершения HTTPInterceptor перед выполнением дополнительных вызовов API?

В Angular 7 я использую HttpInterceptor для получения токена Bearer, который необходимо добавить в качестве заголовка к любым вызовам API (например, getOnLoadData()). Всякий раз, когда я пытаюсь выполнить вызов getOnLoadData(), перехватчик вызывается успешно, но значение токена продолжает возвращаться как нулевое, поскольку оно выполняется до завершения перехватчика.

Я пытаюсь использовать async/await, чтобы сделать вызов асинхронным, но похоже, что я делаю это неправильно, и я не могу понять, где ошибка. Я довольно новичок в Angular. Я искал довольно много разных примеров, но не могу понять это.

Перехватчик HTTP

intercept(req: HttpRequest<any>, next: HttpHandler) {
    return from(this.handle(req, next))
  }

  async handle(req: HttpRequest<any>, next: HttpHandler) {
    const result = await this.getBearerToken();

    const tokenizedReq = req.clone({
      setHeaders: {
        Authorization: `Bearer ${result}`
      }
    })

    return next.handle(tokenizedReq).toPromise();
  }

Вызов API

async getOnLoadData() {
    await this.tokenInterceptorService.getBearerToken()

    return this.httpClient.get('/initialloaddata', {
      headers
    });
  }

person user3767997    schedule 08.04.2020    source источник


Ответы (1)


Вам нужно создать поток данных, который начинается с получения токена аутентификации и заканчивается исправленным запросом, если это необходимо.

public intercept(request: HttpRequest<void>, next: HttpHandler): Observable<HttpEvent<void>> {
    return from(this.getBearerToken()).pipe(
        switchMap(token =>
            iif(
                () => !token,
                of(request),
                of(
                    request.clone({
                        setHeaders: {
                            'Authorization': `Bearer ${token}`,
                        },
                    }),
                ),
            ),
        ),
        switchMap(clonedRequest => next.handle(clonedRequest)),
    );
}

from знает, как преобразовать обещание в наблюдаемый поток данных. поэтому он решает его и передает результат (токен) в канал. затем у нас есть switchMap, которые проверяют наличие токена. Если нет (оператор iif), возвращает исходный запрос без изменений (2-й аргумент iif), если да, то возвращает клон с нужным заголовком. После этого у нас есть еще один switchMap, который просто переключает поток данных на next.handler.

person satanTime    schedule 08.04.2020
comment
Есть ли шанс, что вы могли бы объяснить это немного больше, пожалуйста? Я не думаю, что слежу за тем, что именно это пытается сделать. - person user3767997; 08.04.2020
comment
конечно, from знает, как преобразовать обещание в наблюдаемый поток данных. поэтому он решает его и передает результат (токен) в канал. затем у нас есть switchMap, которые проверяют наличие токена. Если нет (оператор iif), возвращает исходный запрос без изменений (2-й аргумент iif), если да, то возвращает клон с нужным заголовком. После этого у нас есть еще один switchMap, который просто переключает поток данных на next.handler. - person satanTime; 08.04.2020
comment
Я сделал это, и я успешно получаю токен, но getOnLoadData() по-прежнему не получает значение токена. Похоже, что они вызываются одновременно, и getOnLoadData() завершается первым, но токен устанавливается как Bearer null и не ждет завершения перехватчика. - person user3767997; 09.04.2020
comment
если вы хотите сначала подождать getOnLoadData и только потом вызывать getBearerToken вам нужно concat вместо from: concat(this.getOnLoadData(), this.getBearerToken()).pipe(skip(1)), а skip игнорировать значение из getOnLoadData. - person satanTime; 09.04.2020
comment
Есть ли более общий способ сделать это? getOnLoadData() — не единственный вызов API, который я делаю. На самом деле мне понадобится каждый вызов API, который я делаю, чтобы дождаться получения токена. - person user3767997; 09.04.2020
comment
Ах, getOnLoadData не принадлежит токену. Его запрос должен иметь токен. Тогда исходный ответ - это то, что вам нужно. Перехватчик применяется ко всем http-вызовам. Вам нужно зарегистрировать его у провайдеров, а затем просто вызвать this.httpClient.get('/initialloaddata') без 2-го параметра с заголовками, перехватчик добавит заголовок, если this.getBearerToken вернул его. - person satanTime; 09.04.2020