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
相关产品推荐
相关产品推荐

