Попытка повторить http запрос после обновления токена с перехватчиком в angular 7 - PullRequest
0 голосов
/ 23 января 2019

Я пытаюсь автоматизировать запросы на обновление токена при получении ошибки 401 с угловым 7.

Между этим я не нахожу много документации о том, как это сделать с угловым 7, и что у меня нетпредыдущее знание angular или rxjs Я становлюсь немного сумасшедшим

Я думаю, что это почти завершено, но по какой-то причине второй next.handle (newReq) не отправляет запрос (в отладчике сети Google Chrome только появляется первый запрос))

Я получаю ответ на обновление и корректно выполняю processLoginResponse (res)

здесь вы можете увидеть мой перехватчик

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

let newReq = req.clone();

return next.handle(req).pipe(
  catchError(error => {
    if (error.status == 401) {
      this._authenticationService.refresh().subscribe(
        res => {
          this._authenticationService.processLoginResponse(res);
          newReq.headers.set("Authorization", "Bearer " + this._authenticationService.authResponse.token)
          return next.handle(newReq)
        },
        error => {
          this._authenticationService.logOut();
        });
    }
    throw error;
  })
);

Ответы [ 2 ]

0 голосов
/ 23 января 2019

Вы должны расстаться со всеми запросами.Например, вы не хотите перехватывать ваш запрос на вход в систему, а также не запрашивать обновление токена.SwitchMap - ваш лучший друг, потому что вам нужно отменить некоторые вызовы, чтобы дождаться обновления вашего токена.

Итак, вы сначала проверяете сообщения об ошибках со статусом 401 (неавторизовано):

return next.handle(this.addToken(req, this.userService.getAccessToken()))
            .pipe(catchError(err => {
                if (err instanceof HttpErrorResponse) {
                    // token is expired refresh and try again
                    if (err.status === 401) {
                        return this.handleUnauthorized(req, next);
                    }

                    // default error handler
                    return this.handleError(err);

                } else {
                    return observableThrowError(err);
                }
            }));

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

  handleUnauthorized (req: HttpRequest<any>, next: HttpHandler): Observable<any> {
        if (!this.isRefreshingToken) {
            this.isRefreshingToken = true;

            // Reset here so that the following requests wait until the token
            // comes back from the refreshToken call.
            this.tokenSubject.next(null);
            // get a new token via userService.refreshToken
            return this.userService.refreshToken()
                .pipe(switchMap((newToken: string) => {
                    // did we get a new token retry previous request
                    if (newToken) {
                        this.tokenSubject.next(newToken);
                        return next.handle(this.addToken(req, newToken));
                    }

                    // If we don't get a new token, we are in trouble so logout.
                    this.userService.doLogout();
                    return observableThrowError('');
                })
                    , catchError(error => {
                        // If there is an exception calling 'refreshToken', bad news so logout.
                        this.userService.doLogout();
                        return observableThrowError('');
                    })
                    , finalize(() => {
                        this.isRefreshingToken = false;
                    })
                );
        } else {
            return this.tokenSubject
                .pipe(
                    filter(token => token != null)
                    , take(1)
                    , switchMap(token => {
                        return next.handle(this.addToken(req, token));
                    })
                );
        }
    }

У нас есть класс класса перехватчика, который проверяет, существует ли уже запрос на обновление токенавыполняется: this.isRefreshingToken = true; потому что вы не хотите иметь несколько запросов на обновление при запуске нескольких неавторизованных запросов.

Так что все, что есть в части if (!this.isRefreshingToken), касается обновления вашего токена и повторения предыдущего запроса.

Все, что обрабатывается в else, предназначено для всех запросов, в то время как ваш userService обновляет токен, возвращается tokenSubject, и когда токен будет готов с this.tokenSubject.next(newToken);, каждый пропущенный запрос будет повторен.

Вот эта статья была источником вдохновениядля перехватчика: https://www.intertech.com/Blog/angular-4-tutorial-handling-refresh-token-with-new-httpinterceptor/

РЕДАКТИРОВАТЬ:

TokenSubject фактически является субъектом поведения: tokenSubject: BehaviorSubject<string> = new BehaviorSubject<string>(null);, что означает, что любой новый подписчик получит текущее значение вstream, который был бы старым токеном последнего вызова, который мы вызываем this.tokenSubject.next(newToken).

При next(null) каждый новый подписчик не запускает часть switchMap, поэтому filter(token => token != null) необходим.

После повторного вызова this.tokenSubject.next(newToken) с новым токеном каждый подписчик запускает часть switchMap со свежим токеном.Надеюсь, что теперь это более ясно

0 голосов
/ 23 января 2019

Вы можете сделать что-то вроде этого:

import { HttpErrorResponse } from '@angular/common/http';

return next.handle(req).pipe(
  catchError((err: any) => {
    if (err instanceof HttpErrorResponse && err.status 401) {
     return this._authenticationService.refresh()
       .pipe(tap(
         (success) => {},
         (err) => {
           this._authenticationService.logOut();
           throw error;
         }
       ).mergeMap((res) => {
         this._authenticationService.processLoginResponse(res);
         newReq.headers.set("Authorization", "Bearer " + this._authenticationService.authResponse.token)
         return next.handle(newReq)
       });
    } else {
      return Observable.of({});
    }
  }
));
...