Angular拦截器:仅在请求失败时如何重试HTTP请求?
解决HTTP拦截器中仅在特定失败场景下重试请求的问题
你遇到的问题核心在于retry(1)是无条件重试——只要Observable抛出错误,它就会重新订阅源Observable(也就是重新发起请求),这才导致了不管什么错误都触发重试,甚至让你误以为成功场景也被调用了两次(实际上成功时Observable不会触发错误,retry不会执行,大概率是其他场景的误解)。我们需要自定义重试逻辑,只在请求失败或返回500状态码时重试。
问题根源拆解
你的当前代码中,retry(1)会对所有错误触发重试,包括401、404这类完全不需要重试的错误,这不仅不符合需求,还会导致无效的重复请求。要实现精准的重试控制,我们需要用retryWhen替代默认的retry操作符。
解决方案:用retryWhen自定义重试规则
RxJS的retryWhen允许你根据错误的类型、状态码来决定是否重试,完美匹配你的需求。修改后的拦截器代码如下:
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { const userDetails: any = this._authTokenService.getUserDetailsFromCookie(); const token = userDetails?.userToken; if (token) { request = request.clone({ setHeaders: { "Authorization": "Bearer " + token, }, }); } return next.handle(request).pipe( timeout(environment.ajaxTimeout), map((event: HttpEvent<any>) => { if (event instanceof HttpResponse) { const status: number = event.status; let responseObject = event.body; responseObject.status = status; // 处理202状态码的token刷新逻辑 if (status === 202) { console.log(responseObject); alert('status 202'); this._cookieService.set( "userToken", responseObject.body.response.data.token ); } } return event; }), // 用retryWhen替代retry,实现条件重试 retryWhen(errors => errors.pipe( // 筛选需要重试的错误场景 filter(error => { return (error instanceof HttpErrorResponse && error.status === 500) || error instanceof TimeoutError || (error instanceof HttpErrorResponse && error.status === 0); // 网络错误(断网等) }), take(1), // 限制重试次数为1次 delay(1000) // 可选:添加1秒延迟,避免短时间重复请求(可根据需求调整) )), catchError((error: HttpErrorResponse | TimeoutError) => { const status = error instanceof HttpErrorResponse ? error.status : -1; if (status === 401 || error instanceof TimeoutError) { this._authTokenService.deleteUserTokenFromCookie(); this._router.navigate(["/login"]); } else if (status === 404) { this._router.navigate(['/page-not-found']); } else if (status === 500) { // 这里的500是重试后仍然失败的情况 this._router.navigate(['/internal-server-error']); } return throwError(() => error); // RxJS 7+推荐写法 }) ); }
关键细节说明
精准筛选重试场景:
通过filter只保留需要重试的错误:500状态码、超时错误、以及状态码为0的网络错误(比如断网、请求无法到达服务器)。像401、404这类重试无意义的错误会直接跳过重试逻辑。控制重试次数:
take(1)确保最多重试1次,和你原本的retry(1)效果对应。成功场景的保障:
成功时Observable不会触发错误通知,retryWhen完全不会执行,所以请求只会发起一次,彻底解决你担心的“成功时重复调用”问题。
额外优化点
- 给
userDetails添加了可选链?.,避免userDetails为null/undefined时出现报错; - 把
throwError(error)改成throwError(() => error),符合RxJS 7及以上版本的规范。
内容的提问来源于stack exchange,提问作者RAHUL KUNDU
相关产品推荐
相关产品推荐

