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

Angular拦截器引发HTTP请求大量循环问题求助

Angular拦截器Token刷新时出现大量请求循环问题排查与解决

我实现了一个Angular拦截器,预期在Access Token过期时自动刷新令牌,并用新令牌继续执行原请求。但现在遇到的问题是:当Token过期时,会触发大量重复请求循环(不是无限循环,执行100+次后停止),Token未过期时则无此问题,无法定位原因。

拦截器代码

let auth = inject(AuthService);
if (!auth.AccessExpired()) return next(req);
return auth.refresh().pipe(
  catchError(() => next(req)),
  switchMap((res) => {
    var response = res as RefreshResponse;
    const newReq = req.clone({
      headers: req.headers.set("Authorization", `Bearer ${response.access_token}`)
    })
    return next(newReq);
  })
)

刷新函数代码

refresh() {
  return this.http.post<RefreshResponse>(this.baseUrl + "refresh", {}, {
    headers: new HttpHeaders().set("refresh_token", this.refreshToken ?? "")
  }).pipe(
    tap(
      res => {
        console.log('responded' + res);
        this.accessToken = res.access_token
      }
    ),
    retry(2),
    catchError((err) => {
      console.log("errored", + err)
      this.logout();
      this.noti.showError("Your session expired, log in again.", 5000);
      return throwError(() => new Error(err));
    })
  )
}

问题原因分析

  1. 刷新请求被拦截器重复捕获:刷新令牌的refresh请求本身也会经过拦截器,此时Access Token已经过期,拦截器会再次触发刷新逻辑,形成循环。
  2. 操作符顺序错误:catchError放在switchMap之前,一旦刷新请求失败,会直接放行原请求,但原请求的Token仍然过期,会再次进入拦截器引发循环。
  3. 重试机制放大循环次数:刷新函数中的retry(2)会让失败的刷新请求重试2次,叠加拦截器的循环逻辑,导致请求次数大幅增加。

解决方案

1. 排除刷新请求不被拦截

在拦截器开头判断请求URL,跳过刷新接口的拦截处理:

let auth = inject(AuthService);
// 跳过刷新请求,避免被拦截器重复处理
if (req.url.includes('refresh')) {
  return next(req);
}
if (!auth.AccessExpired()) return next(req);
return auth.refresh().pipe(
  switchMap((res) => {
    const response = res as RefreshResponse;
    const newReq = req.clone({
      headers: req.headers.set("Authorization", `Bearer ${response.access_token}`)
    })
    return next(newReq);
  }),
  catchError(() => {
    // 刷新失败后可根据业务处理,比如跳转到登录页,这里暂时放行原请求
    return next(req);
  })
)

2. 调整操作符顺序

将catchError移到switchMap之后,确保只有在刷新请求失败时才执行错误逻辑,避免提前放行过期Token的请求。

3. 控制刷新请求并发(可选)

添加状态标志,确保同一时间只有一个刷新请求在执行,避免多请求触发多次刷新:

private isRefreshing = false;
private refreshSubject = new Subject<any>();

refresh() {
  if (this.isRefreshing) {
    return this.refreshSubject.asObservable();
  }
  this.isRefreshing = true;
  return this.http.post<RefreshResponse>(this.baseUrl + "refresh", {}, {
    headers: new HttpHeaders().set("refresh_token", this.refreshToken ?? "")
  }).pipe(
    tap(res => {
      console.log('responded', res);
      this.accessToken = res.access_token;
      this.isRefreshing = false;
      this.refreshSubject.next(res);
      this.refreshSubject.complete();
    }),
    retry(2),
    catchError((err) => {
      console.log("errored", err);
      this.isRefreshing = false;
      this.refreshSubject.error(err);
      this.refreshSubject.complete();
      this.logout();
      this.noti.showError("Your session expired, log in again.", 5000);
      return throwError(() => new Error(err));
    })
  )
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 20:23:12