Angular 8中next.handle重发请求失效问题求助
Token过期返回401后刷新Token但未自动重发请求的问题
问题描述
Token过期返回401状态码后,获取刷新Token的API已触发,但内部的next.handle并未重发原请求,期望调用refreshToken API后自动重发role API请求。
当前代码
export class InterceptService implements HttpInterceptor { isRefreshingToken = false; isUserLoggedIn$: Observable<boolean> = this.store.pipe(select(isLoggedIn)); constructor(public auth: AuthService, private store: Store<AppState>,http: HttpClient) {} private addTokenHeader(request: HttpRequest<any>, token: string) { request = request.clone({ setHeaders: { Authorization: `Bearer ${token})}` } }); return request; } intercept( request: HttpRequest<any>, next: HttpHandler ): Observable<HttpEvent<any>> { request = request.clone({ setHeaders: { Authorization: `Bearer ${localStorage.getItem(environment.authTokenKey)}` } }); return next.handle(request).pipe( tap( event => { if (event instanceof HttpResponse) { } }, error => { if(error.status === 401 && !this.isRefreshingToken && this.isUserLoggedIn$) { this.isRefreshingToken = true; this.auth.getRefreshToken().subscribe( (authToken: IAuthToken) => { this.store.dispatch(new TokenSet({authToken})); let newReq = this.addTokenHeader(request, authToken.token); return next.handle(newReq); } ); } else { this.isRefreshingToken = false; } } ) ); } }
当前现象

解决方案
问题核心在于tap操作符仅用于副作用处理,不会修改或替换原Observable流,内部订阅getRefreshToken后返回的next.handle(newReq)并未被加入到原响应流中,因此重发请求不会被触发。同时代码还存在两个细节问题:addTokenHeader里的Authorization头多了一个右括号,以及直接判断Observable类型的isUserLoggedIn$不会得到实际的登录状态。
修改后的完整代码:
export class InterceptService implements HttpInterceptor { isRefreshingToken = false; isUserLoggedIn$: Observable<boolean> = this.store.pipe(select(isLoggedIn)); constructor(public auth: AuthService, private store: Store<AppState>, private http: HttpClient) {} private addTokenHeader(request: HttpRequest<any>, token: string) { return request.clone({ setHeaders: { Authorization: `Bearer ${token}` // 修复多余的右括号 } }); } intercept( request: HttpRequest<any>, next: HttpHandler ): Observable<HttpEvent<any>> { // 给请求添加Token头 const token = localStorage.getItem(environment.authTokenKey); if (token) { request = this.addTokenHeader(request, token); } return next.handle(request).pipe( catchError(error => { // 仅处理401、未在刷新Token、且用户处于登录状态的情况 if (error.status === 401 && !this.isRefreshingToken) { return this.isUserLoggedIn$.pipe( switchMap(isLoggedIn => { if (!isLoggedIn) { // 用户未登录,直接抛出错误 return throwError(() => error); } this.isRefreshingToken = true; // 刷新Token return this.auth.getRefreshToken().pipe( switchMap((authToken: IAuthToken) => { this.store.dispatch(new TokenSet({ authToken })); localStorage.setItem(environment.authTokenKey, authToken.token); // 重发原请求 const newReq = this.addTokenHeader(request, authToken.token); return next.handle(newReq); }), // 无论刷新成功或失败,重置刷新状态 finalize(() => { this.isRefreshingToken = false; }) ); }) ); } // 其他错误直接抛出 return throwError(() => error); }) ); } }
关键修改点
- 替换
tap为catchError:捕获401错误后返回新的Observable流,确保重发请求能被订阅处理 - 正确处理
isUserLoggedIn$:通过switchMap订阅登录状态Observable,获取实际的布尔值 - 修复Authorization头语法错误:移除多余的右括号
- 添加
finalize重置状态:无论刷新Token成功或失败,都重置isRefreshingToken,避免后续请求被阻塞 - 更新本地存储Token:刷新Token后同步更新localStorage中的Token,确保后续请求使用新Token
内容的提问来源于stack exchange,提问作者Asim Sapkota
相关产品推荐
相关产品推荐

