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

Angular 4错误处理:Token过期自动刷新后继续提交操作

Angular中实现Token过期自动刷新的错误处理器

看起来你已经在尝试用ErrorHandler来处理Token过期的场景了,我来帮你完善这个实现,确保它能稳定处理401错误、自动刷新Token并重试请求,同时避开常见的坑。

核心需求回顾

  • 提交请求到API时,检测到Token过期(返回401状态码)
  • 自动调用刷新Token接口获取新Token,并存入localStorage
  • 刷新成功后,自动重试原本失败的请求
  • 刷新失败时,引导用户重新登录

完整的AuthErrorHandler实现

先补全你的代码,同时解决循环依赖、重复刷新等问题:

import { Injectable, ErrorHandler, Injector } from '@angular/core';
import { HttpClient, HttpRequest, HttpHeaders } from '@angular/common/http';
import { LoginService } from './login.service';
import { Router } from '@angular/router';
import { Observable, of } from 'rxjs';
import { catchError, switchMap } from 'rxjs/operators';

@Injectable()
export class AuthErrorHandler implements ErrorHandler {
  // 防止多个401请求同时触发刷新Token
  private isRefreshing = false;
  // 存储等待重试的请求
  private pendingRequests: Array<(token: string) => void> = [];

  constructor(
    private injector: Injector,
    private loginService: LoginService,
    private router: Router
  ) {}

  handleError(error: any): void {
    // 处理HTTP请求错误(确保是HTTP ErrorResponse类型)
    if (error.rejection && error.rejection.status === 401) {
      const httpError = error.rejection;
      const requestUrl = httpError.url;

      // 排除刷新Token本身的请求,避免无限循环
      if (requestUrl.includes('/refresh-token')) {
        this.handleRefreshFailure();
        return;
      }

      // 如果正在刷新,把当前请求加入等待队列
      if (this.isRefreshing) {
        this.pendingRequests.push((newToken: string) => {
          this.retryRequest(httpError, newToken);
        });
        return;
      }

      this.isRefreshing = true;

      // 调用LoginService刷新Token
      this.loginService.refreshToken().pipe(
        catchError(() => {
          // 刷新失败,清空Token并跳转登录
          this.handleRefreshFailure();
          return of(null);
        })
      ).subscribe((newToken: string | null) => {
        this.isRefreshing = false;

        if (newToken) {
          // 存储新Token到localStorage
          localStorage.setItem('authToken', newToken);
          // 重试等待队列中的所有请求
          this.pendingRequests.forEach(callback => callback(newToken));
          this.pendingRequests = [];
          // 重试当前失败的请求
          this.retryRequest(httpError, newToken);
        }
      });
    } else {
      // 处理其他类型的错误
      console.error('未处理的错误:', error);
    }
  }

  private retryRequest(error: any, newToken: string): void {
    const http = this.injector.get(HttpClient);
    const originalRequest = error.error.request || error.request;

    // 克隆原请求,替换新的Authorization头
    const clonedRequest = new HttpRequest(
      originalRequest.method,
      originalRequest.url,
      originalRequest.body,
      {
        headers: new HttpHeaders({
          'Authorization': `Bearer ${newToken}`,
          ...originalRequest.headers
        }),
        withCredentials: originalRequest.withCredentials
      }
    );

    // 重试请求
    http.request(clonedRequest).subscribe();
  }

  private handleRefreshFailure(): void {
    // 清空本地Token
    localStorage.removeItem('authToken');
    localStorage.removeItem('refreshToken');
    // 跳转登录页
    this.router.navigate(['/login']);
  }
}

关键细节说明

  • 避免循环依赖:使用Injector延迟获取HttpClient,因为LoginService可能依赖HttpClient,直接注入会导致循环依赖问题。
  • 防止重复刷新:用isRefreshing标志位控制,同时把多个等待的请求存入队列,刷新完成后统一重试,避免重复调用刷新接口。
  • 排除刷新请求本身:避免刷新Token的请求返回401时进入无限循环。
  • 错误边界处理:刷新Token失败时,清空本地Token并跳转到登录页,避免用户停留在未授权状态。

配置LoginService的refreshToken方法

你的LoginService需要提供刷新Token的方法,示例如下:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class LoginService {
  constructor(private http: HttpClient) {}

  refreshToken(): Observable<string> {
    // 从localStorage获取提前存储的refreshToken
    const refreshToken = localStorage.getItem('refreshToken');
    return this.http.post<{ token: string, refreshToken: string }>('/api/refresh-token', { refreshToken })
      .pipe(
        map(response => {
          // 同时更新refreshToken
          localStorage.setItem('refreshToken', response.refreshToken);
          return response.token;
        })
      );
  }
}

注册错误处理器

最后要在AppModule中注册这个错误处理器,让全局生效:

import { NgModule, ErrorHandler } from '@angular/core';
import { AuthErrorHandler } from './auth-error-handler';

@NgModule({
  providers: [
    { provide: ErrorHandler, useClass: AuthErrorHandler }
  ]
})
export class AppModule { }

这样就能实现你需要的Token过期自动刷新、存储和请求重试的完整流程了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 08:20:58