[英]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.