Вы должны расстаться со всеми запросами.Например, вы не хотите перехватывать ваш запрос на вход в систему, а также не запрашивать обновление токена.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
со свежим токеном.Надеюсь, что теперь это более ясно