首页 > 解决方案 > Angular HTTP 拦截器 - 无法从内部订阅返回

问题描述

我正在尝试实现一个 HTTP 拦截器,所以当令牌过期时,我得到了 refresh_token,然后我调用return next.handle(request);了,但似乎我无法从主管道返回。如果我尝试以 /API_URL/page1 的身份访问我的页面并且返回 401 状态,那么我将获得刷新令牌,但 /API_URL/page1 应该再次被调用,但事实并非如此。我用"rxjs": "6.5.5". 这是我的代码:

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const currentUser = this.lsService.getItem('currentUser') as any;

    if(currentUser && currentUser.accessToken && this.checkIfUrlNeedsToken(request)) {
      request = request.clone({ headers: request.headers.set('Authorization', currentUser.accessToken) });
    }

    return next.handle(request)
      .pipe(tap(
        (event: HttpEvent<any>) => {
          if (event instanceof HttpResponse) {
            // do stuff with response if you want
          }
        }, (err: any) => {
          if(this.isRefreshTokenExpiredError(err)) {
            this.redirectToLogin();
          } else if(this.isAuthError(err) && this.checkIfUrlNeedsToken(request)) {
            return this.authService.getNewToken().subscribe((response: User): Observable<HttpEvent<any>> => {
              this.lsService.setItem('currentUser', response);  //  Rewrite the current user
              request = request.clone({ headers: request.headers.set('Authorization', response.accessToken) });
              return next.handle(request);
            }); 
          } else {
            this.redirectToLogin();
          }
        }
      ));
  }

请指教。谢谢!

标签: angularrxjs

解决方案


您不能从订阅返回值 - 您必须使用 aswitchMap或 a从管道内返回可观察对象concatMap

此外,tap正在执行副作用。您不能从tap. 在您的情况下,您将需要从catchError.

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
  const currentUser = this.lsService.getItem('currentUser') as any;

  if(currentUser && currentUser.accessToken && this.checkIfUrlNeedsToken(request)) {
    request = request.clone({ headers: request.headers.set('Authorization', currentUser.accessToken) });
  }

  return next.handle(request)
    .pipe(
      tap((event: HttpEvent<any>) => {
        if (event instanceof HttpResponse) {
          // do stuff with response if you want
        }
      }, (err: any) => {
        if (this.isRefreshTokenExpiredError(err)) {
          this.redirectToLogin();
        } else if(this.isAuthError(err) && this.checkIfUrlNeedsToken(request)) {
          // handle in catchError
        } else {
          this.redirectToLogin();
        }
      }),
      catchError((err: any) => {
        if(!this.isAuthError(err) || !this.checkIfUrlNeedsToken(request)) {
          return throwError(err);
        }

        return this.authService.getNewToken().pipe(
          switchMap((response: User): Observable<HttpEvent<any>> => {
            //  Rewrite the current user
            this.lsService.setItem('currentUser', response); 
            request = request.clone({ headers: request.headers.set('Authorization', response.accessToken) });
            return next.handle(request);
          }); 
      })
    );
  }

通过管道的错误操作流程现在类似于:

  • 点击:必要时重定向
  • catchError:必要时获取新令牌
    • switchMap:重新运行http请求

我已将 observable 移动到 a switchMapinside acatchError中。我没有改变你在里面的任何逻辑tap- 现在可能可以简化了。


推荐阅读