繁体   English   中英

angular http 拦截器在令牌刷新后不再调用请求

[英]angular http interceptor not calling request again after token refresh

我的项目中有一个 http 拦截器,它处理访问令牌的刷新。
当用户的访问令牌过期时,请求将收到 401 错误,在这种情况下,这个 function 应该处理所有事情,刷新令牌并使用新的访问令牌再次调用请求。

这是function的调用:

return next.handle(request).pipe(catchError((error) => {
  if (error instanceof HttpErrorResponse && error.status === 401) {
    return this.handle401Error(request, next);
  } else {
    return throwError(error);
  }
}));

还有句柄401:

  handle401Error(request: HttpRequest<any>, next: HttpHandler): any {
    if (!this.isRefreshing) {
      this.isRefreshing = true;
      this.refreshTokenSubject.next(null);

      this.auth.refreshAccessToken().then((token: Token) => {
        this.isRefreshing = false;
        this.refreshTokenSubject.next(token.access_token);
        return next.handle(this.addToken(request, token.access_token));
      });
    } else {
      return this.refreshTokenSubject.pipe(
          filter((token) => token !== null),
          take(1),
          switchMap((token) => {
            return next.handle(this.addToken(request, token));
          }));
    }
  }

我从一篇文章中创建了拦截器,它应该可以正常工作,令牌刷新就像一个魅力,但是

return next.handle(this.addToken(request, token.access_token));

哪个应该使用现在有效的令牌再次调用请求只是不调用它。

问题

this.auth.refreshAccessToken()返回一个promise (我假设给定 .then .then() )。

解释

以防万一您不熟悉 Promise,它们是处理异步代码的通用系统。 这是文档的链接

this.auth.refreshAccessToken().then()将 function 作为参数,通常,您提供了一个匿名箭头 function (token: Token) => {... }

当你return next.handle(this.addToken(request, token.access_token)); ,您在箭头 function 内,因此您实际上并没有从handle401Error()返回值,而是将值返回给 .then( .then()

.then()确实返回一个值,但您目前没有返回该值。

您可以在 else 块中看到此操作正确完成:

return this.refreshTokenSubject.pipe(                          <-- top-level return
          filter((token) => token !== null),
          take(1),
          switchMap((token) => {
            return next.handle(this.addToken(request, token)); <-- nested return
          }));
    }

解决方案

TLDR;

 return from(this.auth.refreshAccessToken()).pipe(switchMap((token: Token) => {
        this.isRefreshing = false;
        this.refreshTokenSubject.next(token.access_token);
        return next.handle(this.addToken(request, token.access_token));
      }));

解释

一件可能使事情变得更容易的小事情,我建议您使用 handle.next handle.next() ) 的返回类型Observable<HttpEvent<any>>代替any作为handle401Error()的返回类型。

您需要做的是从this.auth.refreshAccessToken().then()内部返回next.handle()的值。

可能有多种方法可以做到这一点,但我会推荐 Angular/RxJS 风格。

正如我之前所说,promise 就像 observables 一样,RxJS (v6+) 提供了一种将 promise 转换为 observable 的方法,例如:

import { from } from 'rxjs';
const observable = from(promise);

您可以使用this.auth.refreshAccessToken()转换为可观察对象:

from(this.auth.refreshAccessToken())

现在我们有了一个 observable,你可能倾向于使用subscribe来获取值,但这不是你想要做的,因为你的拦截器正在返回一个最终的 observable,它会在其他地方订阅。

您可以做的是使用pipe ,它允许您使用 RxJS 提供的许多运算符。 在这种情况下,您想要等待您的第一个可观察的refreshAccessToken()发出,然后您想要返回next.handle() 此任务常用的运算符是switchMap

您会注意到您的 else 块实际上正在使用它:

return this.refreshTokenSubject.pipe(
          filter((token) => token !== null),
          take(1),
          switchMap((token) => {                                <-- switchMap
            return next.handle(this.addToken(request, token));
          }));
    }

switchMap()等待第一个 observable 发射,然后将值输出到您的回调 function,期望您返回另一个 observable。 在您的情况下,这意味着您将then()替换为pipe(switchMap())

如 TLDR 所示:

 return from(this.auth.refreshAccessToken()).pipe(switchMap((token: Token) => {
        this.isRefreshing = false;
        this.refreshTokenSubject.next(token.access_token);
        return next.handle(this.addToken(request, token.access_token));
      }));

这应该可以解决您的问题,如果这不起作用,请在下面评论。

句柄 handle401Error function 不应返回 next.handle(...)。 无论如何,您已经在拦截 function 中返回了该内容。 而是像正常一样将令牌添加到请求中并返回 of(null); 在句柄401错误function。 除非您想抛出自定义错误,否则 catchError function 应该始终返回 null。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM