Angular 5 HttpInterceptor拦截器重发请求代码被忽略,求排查原因
问题根源分析
你的拦截器核心问题出在错误处理的流控制逻辑上,我给你拆解下关键问题点:
do操作符的误用:你用do(RxJS 5中的副作用操作符,对应后续版本的tap)来处理错误,但do仅用于执行日志打印这类副作用,不会修改或替换原Observable流。所以你在do的错误回调里写的重发请求逻辑,外层的订阅者根本接收不到,自然不会生效。直接调用
subscribe而非合并流:刷新Token时你直接调用了this.auth.refreshToken().subscribe(...),这会创建一个独立的订阅,但这个订阅的结果不会合并到拦截器返回的主Observable流中。上游组件/服务订阅的是拦截器返回的流,所以内部订阅的请求结果根本传不回去。
修正后的拦截器代码
下面是调整后的完整代码,我标注了关键修改点:
import { Injectable } from '@angular/core'; import { HttpRequest, HttpHandler, HttpResponse, HttpErrorResponse, HttpEvent, HttpInterceptor } from '@angular/common/http'; import { AuthService } from './auth.service'; import { Observable } from 'rxjs/Observable'; // 导入所需的RxJS操作符 import 'rxjs/add/operator/catch'; import 'rxjs/add/operator/switchMap'; import 'rxjs/add/observable/throw'; @Injectable() export class TokenInterceptor implements HttpInterceptor { constructor(public auth: AuthService) {} intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { console.log("Interceptor engaged: " + this.auth.getAccessToken()); // 先给请求添加Token const authReq = request.clone({ setHeaders: { Authorization: `Bearer ${this.auth.getAccessToken()}` } }); // 用catch替代do处理错误,catch可以返回新的Observable替换错误流 return next.handle(authReq).catch((err: any) => { if (err instanceof HttpErrorResponse) { // 只针对401处理Token刷新,500一般是服务器内部错误,和Token无关 if (err.status === 401) { console.log("attempting refresh."); // 防无限循环:如果当前请求就是刷新Token的接口,直接抛出错误跳转登录 if (request.url.includes('/refresh-token')) { console.log("Refresh token failed, redirecting to login..."); // 这里添加跳转登录的逻辑,比如this.auth.logout()后跳转到/login路由 return Observable.throw(err); } // 使用switchMap切换到刷新Token的流,而非直接subscribe return this.auth.refreshToken() .switchMap(() => { console.log("refresh success"); // 刷新成功后,克隆原请求并使用新Token重新发送 const newAuthReq = request.clone({ setHeaders: { Authorization: `Bearer ${this.auth.getAccessToken()}` } }); // 返回重发的请求流,这个流会被上游订阅者接收 return next.handle(newAuthReq); }) .catch((refreshErr) => { // 刷新Token失败,跳转登录 console.log("Refresh token failed, redirecting to login..."); // 这里添加跳转登录的逻辑 return Observable.throw(refreshErr); }); } } // 其他错误直接抛出 return Observable.throw(err); }); } }
关键修改说明
- 用
catch替代do处理错误:catch操作符允许我们捕获错误后,返回一个新的Observable继续流的传递,这样重发请求的结果就能被上游订阅者接收到。 - 用
switchMap合并异步流:刷新Token是异步操作,switchMap会等待刷新Token的Observable完成,再切换到重发请求的Observable,把新流返回给上游。 - 添加防无限循环逻辑:判断当前请求是否是刷新Token的接口,如果是就直接抛出错误,避免因刷新失败导致的无限重试。
- 分离错误分支逻辑:把刷新成功、失败的逻辑分开,代码更清晰,也能正确处理各种异常场景。
额外建议
- 建议只针对
401状态码处理Token刷新,500通常是服务器内部错误,和Token无关,不需要触发刷新逻辑。 - 可以添加一个全局标志位,防止多个并发请求同时触发Token刷新(比如多个请求同时返回401,只调用一次刷新接口),避免重复请求。
内容的提问来源于stack exchange,提问作者Behrooz
相关产品推荐
相关产品推荐

