angular - 在拦截器中刷新 token 时管道 catchError 不起作用

标签 angular ecmascript-6 interceptor rxjs6

这可能是一个愚蠢的问题,但我创建了一个拦截器,如果响应失败并显示代码 401,我会捕获该拦截器并使用 api 刷新 token 。它工作正常,但如果由于某种原因刷新 token api 也失败,我也想捕获错误,这里是代码

@Injectable()
export class TokenInterceptor implements HttpInterceptor {

    constructor(private authService: AuthService) { }

    isRefreshingToken: boolean = false;
    tokenSubject: BehaviorSubject<string> = new BehaviorSubject<string>(null);

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any> | any> {

        return next.handle(this.addTokenToRequest(request, this.authService.getJWT()))
            .pipe(
                catchError(err => {
                    if (err instanceof HttpErrorResponse) {
                        switch ((<HttpErrorResponse>err).status) {
                            case 401:
                                return this.handle401Error(request, next);
                            case 400:
                                return <any>this.authService.logout();
                        }
                    } else {
                        return throwError(err);
                    }
                }));
    }

    private addTokenToRequest(request: HttpRequest<any>, token: string): HttpRequest<any> {
        return request.clone({ setHeaders: { Authorization: `Bearer ${token}` } });
    }

    private handle401Error(request: HttpRequest<any>, next: HttpHandler) {

        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);

            return this.authService.refreshToken()
                .pipe(
                    switchMap((refreshToken: any) => {
                        console.log('here');
                        if (refreshToken) {
                            this.tokenSubject.next(refreshToken.token);
                            this.authService.setJWT(refreshToken.token);
                            this.authService.setRefreshToken(refreshToken.refreshToken);
                            // localStorage.setItem('currentUser', JSON.stringify(user));
                            return next.handle(this.addTokenToRequest(request, refreshToken.token));
                        }else{

                        }

                        return <any>this.authService.logout();
                    }),
                    catchError(err => {
                        console.log('here', err);
                        return <any>this.authService.logout();
                    }),
                    finalize(() => {
                        console.log('here');
                        this.isRefreshingToken = false;
                    })
                );
        } else {
            this.isRefreshingToken = false;

            return this.tokenSubject
                .pipe(filter(token => token != null),
                    take(1),
                    switchMap(token => {
                        return next.handle(this.addTokenToRequest(request, token));
                    }));
        }
    }
}

注意调用 return this.authService.refreshToken() 的行,如果 refreshToken() 失败,它不会到达 catchError

我知道,只有当原始请求失败时,它才可能到达 catchError。所以在服务中我尝试从刷新 token api 捕获错误,代码如下:

refreshToken(): Observable<any> {
    let jwt = this.getJWT();
    let refreshToken = this.getRefreshToken();

    debugger;
    return this.http.post<any>(this.api.REFRESH_TOKEN, { refreshToken: refreshToken, expiredToken: jwt })
      .pipe(
        map(result => {
          console.log('here');
          debugger;
          return result;
        }),
        catchError(e => {
          debugger;
          console.log(e, 'here');
          return null;
        })
      );
  }

如果刷新 token api 失败,它甚至不会达到这一点,我的问题是如何从刷新 token api 捕获错误并将用户重定向到登录。 谢谢。

最佳答案

你必须将你的 catchError block 移到你的 switchMap 中,如果 catchError 开启,那么将 catchError 放在 Observable 管道的第一级并不是一个好的错误处理方法Observable 管道方法的第一层,将调用 finalize 运算符

.pipe(switchMap((refreshToken: any) => {
      console.log('here');
      if (refreshToken) {
       this.tokenSubject.next(refreshToken.token);
       this.authService.setJWT(refreshToken.token);
       this.authService.setRefreshToken(refreshToken.refreshToken);
       // localStorage.setItem('currentUser', JSON.stringify(user));
       return next.handle(this.addTokenToRequest(request, refreshToken.token)).pipe(
           catchError(err => {
               console.log('here', err);
               return <any>this.authService.logout();
            })
       );
     }
     else{}

     return <any>this.authService.logout();
}),

 finalize(() => {
 console.log('here');
 this.isRefreshingToken = false;
})

关于angular - 在拦截器中刷新 token 时管道 catchError 不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56832151/

相关文章:

javascript - Ionic 2 Angular 2 全局导入扩展方法

angular - 从 effects : difference between different rxjs operators 调度多个 Action

javascript - 将父作用域添加到匿名函数 - JavaScript 到 Angular 2

javascript - Angular 2+ 窗口属性未定义

javascript - es6 类方法内的 "This"

scala - 将未经身份验证的请求发送到不同的操作路由

java - 422 状态码 - ReaderInterceptor Jersey

java - 拦截服务器java中的HTTP请求

javascript - 编译TypeScript时如何处理外部模块?

javascript - 从非类继承 ES6/TS 类