You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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+推荐写法
    })
  );
}

关键细节说明

  1. 精准筛选重试场景:
    通过filter只保留需要重试的错误:500状态码、超时错误、以及状态码为0的网络错误(比如断网、请求无法到达服务器)。像401、404这类重试无意义的错误会直接跳过重试逻辑。

  2. 控制重试次数:
    take(1)确保最多重试1次,和你原本的retry(1)效果对应。

  3. 成功场景的保障:
    成功时Observable不会触发错误通知,retryWhen完全不会执行,所以请求只会发起一次,彻底解决你担心的“成功时重复调用”问题。

额外优化点

  • 给userDetails添加了可选链?.,避免userDetails为null/undefined时出现报错;
  • 把throwError(error)改成throwError(() => error),符合RxJS 7及以上版本的规范。

内容的提问来源于stack exchange,提问作者RAHUL KUNDU

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 15:13:15