简体   繁体   English

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

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

I have a http interceptor in my project, it handles the refreshing of the access token.我的项目中有一个 http 拦截器,它处理访问令牌的刷新。
When a user's access token expires the request will get a 401 error, in that case, this function should handle everything, refreshing the token and calling the request again, with the new access token.当用户的访问令牌过期时,请求将收到 401 错误,在这种情况下,这个 function 应该处理所有事情,刷新令牌并使用新的访问令牌再次调用请求。

Here is the calling of the 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);
  }
}));

And the handle401:还有句柄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));
          }));
    }
  }

I created the interceptor from an article, which should work fine, the token refreshing works like a charm, but the我从一篇文章中创建了拦截器,它应该可以正常工作,令牌刷新就像一个魅力,但是

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

Which should call the request again with the now valid token just doesn't call it.哪个应该使用现在有效的令牌再次调用请求只是不调用它。

The problem问题

this.auth.refreshAccessToken() returns a promise (I assume given the .then() ). this.auth.refreshAccessToken()返回一个promise (我假设给定 .then .then() )。

Explanation解释

Just in case you are not familiar with promises, they are a common system for handling asynchronous code.以防万一您不熟悉 Promise,它们是处理异步代码的通用系统。 Here is a link to the docs .这是文档的链接

The this.auth.refreshAccessToken().then() takes a function as an argument, as is common, you have provided an anonymous arrow function (token: Token) => {... } . this.auth.refreshAccessToken().then()将 function 作为参数,通常,您提供了一个匿名箭头 function (token: Token) => {... }

When you do return next.handle(this.addToken(request, token.access_token));当你return next.handle(this.addToken(request, token.access_token)); , you are inside the arrow function, so you are not actually returning a value from handle401Error() , you are returning a value to .then() . ,您在箭头 function 内,因此您实际上并没有从handle401Error()返回值,而是将值返回给 .then( .then()

.then() does return a value, but you aren't returning that at the moment. .then()确实返回一个值,但您目前没有返回该值。

You can see this done correctly in your else block:您可以在 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
          }));
    }

The solution解决方案

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

Explanation解释

A small thing that might make things easier, I would recommend instead of any as the return type of handle401Error() you use the return type of handle.next() which is Observable<HttpEvent<any>> .一件可能使事情变得更容易的小事情,我建议您使用 handle.next handle.next() ) 的返回类型Observable<HttpEvent<any>>代替any作为handle401Error()的返回类型。

What you need to do is return the value of next.handle() from inside this.auth.refreshAccessToken().then() .您需要做的是从this.auth.refreshAccessToken().then()内部返回next.handle()的值。

There are probably multiple ways to do this, but I'm going to recommend the Angular/RxJS style.可能有多种方法可以做到这一点,但我会推荐 Angular/RxJS 风格。

As I said before, promises are like observables and RxJS (v6+) provides a way to convert a promise to an observable, for example:正如我之前所说,promise 就像 observables 一样,RxJS (v6+) 提供了一种将 promise 转换为 observable 的方法,例如:

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

You can use this to convert this.auth.refreshAccessToken() to an observable:您可以使用this.auth.refreshAccessToken()转换为可观察对象:

from(this.auth.refreshAccessToken())

Now we have an observable, you might be inclined to get the value out using subscribe but that is not what you want to do because your interceptor is returning a final observable that gets subscribed to elsewhere.现在我们有了一个 observable,你可能倾向于使用subscribe来获取值,但这不是你想要做的,因为你的拦截器正在返回一个最终的 observable,它会在其他地方订阅。

What you can do instead is use pipe , which allows you to use a number of operators provided by RxJS.您可以做的是使用pipe ,它允许您使用 RxJS 提供的许多运算符。 In this case, you want to wait for your first observable refreshAccessToken() to emit and then you want to return next.handle() .在这种情况下,您想要等待您的第一个可观察的refreshAccessToken()发出,然后您想要返回next.handle() The commonly used operator for this task is switchMap .此任务常用的运算符是switchMap

You will notice that your else block is actually using this:您会注意到您的 else 块实际上正在使用它:

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

switchMap() waits for the first observable to emit, and then outputs the value into your callback function, expecting you to return another observable. switchMap()等待第一个 observable 发射,然后将值输出到您的回调 function,期望您返回另一个 observable。 In you case, this would mean that your replace then() with pipe(switchMap()) .在您的情况下,这意味着您将then()替换为pipe(switchMap())

As shown in the TLDR:如 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));
      }));

This should resolve your issue, please comment below if this doesn't work.这应该可以解决您的问题,如果这不起作用,请在下面评论。

The handle handle401Error function shouldn't return next.handle(...).句柄 handle401Error function 不应返回 next.handle(...)。 You already have that covered being returned in the intercept function anyways.无论如何,您已经在拦截 function 中返回了该内容。 Instead add the token to the request like normal and return of(null);而是像正常一样将令牌添加到请求中并返回 of(null); in the handle401Error function.在句柄401错误function。 The catchError function should always return null unless you want to throw a custom error.除非您想抛出自定义错误,否则 catchError function 应该始终返回 null。

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

相关问题 Angular 4 令牌刷新后拦截器重试请求 - Angular 4 Interceptor retry requests after token refresh Angular 7 中的 JWT 刷新令牌 - API 在 HTTP 拦截器中多次调用 - JWT Refresh Token in Angular 7 - API's calling multiple times in HTTP interceptor 在 HTTP 拦截器成功刷新令牌并执行请求后重试组件中的订阅 - Retry a subscription in component after HTTP Interceptor successful refresh the token and do the request Angular 10:获取刷新令牌后重复同样的http请求 - Angular 10: repeat the same http request after obtaining the refresh token 拦截器Angular 6中的刷新令牌(JWT) - Refresh token (JWT) in interceptor Angular 6 拦截器中的角度http请求时间 - Angular http request time in interceptor 在触发请求之前刷新Interceptor中的令牌 - Refresh Token in Interceptor before request is fired 令牌拦截器不再发送原始请求(Angular、Node.js) - Token interceptor does not send the original request again (Angular, Node.js) Angular 拦截器 - 如果刷新令牌拦截器失败,我如何注销? - Angular interceptor - how do I logout if refresh token interceptor fails? 刷新令牌的工作方式以及上次失败的http请求如何再次调用,从而给了401…? - How refresh token works and how last failed http request called again which gave 401…?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM