簡體   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