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