在 Angular 应用开发中,HTTP 请求是与后端交互的核心环节。实际项目中,我们常常需要为所有请求统一添加认证 Token、统一处理响应错误(如 401 未授权、500 服务器错误),如果每个请求都单独处理这些逻辑,不仅代码冗余,还容易出现遗漏。Angular 的 HTTP 拦截器(HTTP Interceptor)正是为解决这类问题而生的最佳方案。
本文将手把手教你实现一个实用的 HTTP 拦截器,完成两大核心功能:
- 为所有 HTTP 请求自动添加 Token 请求头
- 统一捕获和处理 HTTP 响应异常
一、HTTP 拦截器核心概念
HTTP 拦截器是 Angular 提供的一种中间件机制,能够拦截应用发出的所有 HTTP 请求和响应,允许我们在请求发送前、响应返回后(或出错时)插入自定义逻辑。
拦截器基于HttpInterceptor接口实现,核心是intercept方法,该方法接收两个参数:
req: HttpRequest<any>:待处理的请求对象(不可直接修改,需克隆后修改)next: HttpHandler:请求处理链路的下一个处理器
二、实现步骤
1. 环境准备
确保你的 Angular 项目已正确配置,建议使用 Angular 12 + 版本(本文示例基于 Angular 16)。
2. 创建拦截器服务
首先创建一个拦截器文件http.interceptor.ts,放在core/interceptors目录下(符合 Angular 最佳实践):
import { Injectable } from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor,
HttpErrorResponse
} from '@angular/common/http';
import { Observable, throwError, catchError } from 'rxjs';
import { Router } from '@angular/router';
import { ToastService } from '../services/toast.service'; // 自定义提示服务(可替换为你的提示组件)
@Injectable()
export class HttpTokenInterceptor implements HttpInterceptor {
constructor(
private router: Router,
private toastService: ToastService // 用于全局提示错误信息
) {}
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
// 1. 请求拦截:统一添加Token请求头
const modifiedReq = this.addTokenToRequest(request);
// 2. 响应拦截:统一处理响应和错误
return next.handle(modifiedReq).pipe(
catchError((error: HttpErrorResponse) => this.handleResponseError(error))
);
}
/**
* 为请求添加Token请求头
* @param request 原始请求对象
* @returns 克隆后的新请求对象
*/
private addTokenToRequest(request: HttpRequest<unknown>): HttpRequest<unknown> {
// 从本地存储获取Token(实际项目中建议封装到AuthService)
const token = localStorage.getItem('auth_token');
// 如果有Token,则克隆请求并添加Authorization头
if (token) {
return request.clone({
setHeaders: {
'Authorization': `Bearer ${token}`, // JWT标准格式
'Content-Type': 'application/json' // 统一设置Content-Type
}
});
}
// 无Token则返回原始请求
return request;
}
/**
* 统一处理响应错误
* @param error HTTP错误响应
* @returns 抛出错误供订阅者处理
*/
private handleResponseError(error: HttpErrorResponse): Observable<never> {
let errorMessage = '未知错误,请稍后重试';
// 根据错误类型分类处理
if (error.error instanceof ErrorEvent) {
// 客户端错误(如网络异常)
errorMessage = `客户端错误:${error.error.message}`;
} else {
// 服务端错误(根据状态码处理)
switch (error.status) {
case 401:
// 未授权:清除Token并跳转到登录页
localStorage.removeItem('auth_token');
this.toastService.error('登录已过期,请重新登录');
this.router.navigate(['/login']);
errorMessage = '登录已过期,请重新登录';
break;
case 403:
errorMessage = '无权限访问该资源';
break;
case 404:
errorMessage = '请求的资源不存在';
break;
case 500:
errorMessage = '服务器内部错误,请联系管理员';
break;
default:
errorMessage = `服务端错误 [${error.status}]:${error.message}`;
}
}
// 全局提示错误信息
this.toastService.error(errorMessage);
// 抛出错误,让请求的订阅者也能捕获
return throwError(() => new Error(errorMessage));
}
}
3. 注册拦截器
创建完拦截器后,需要在 Angular 的根模块(app.module.ts)中注册,使其生效:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HttpTokenInterceptor } from './core/interceptors/http.interceptor';
import { ToastService } from './core/services/toast.service'; // 引入提示服务
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [
ToastService,
// 注册HTTP拦截器
{
provide: HTTP_INTERCEPTORS,
useClass: HttpTokenInterceptor,
multi: true // 允许多个拦截器(重要!)
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
4. 补充说明:自定义提示服务(ToastService)
上述代码中用到了ToastService,这是一个自定义的全局提示服务,你可以根据项目实际情况替换为 NG-ZORRO、PrimeNG 等 UI 库的提示组件,或实现一个简易版:
// core/services/toast.service.ts
import { Injectable } from '@angular/core';
import { ToastrService } from 'ngx-toastr'; // 以ngx-toastr为例
@Injectable({
providedIn: 'root'
})
export class ToastService {
constructor(private toastr: ToastrService) {}
// 错误提示
error(message: string): void {
this.toastr.error(message, '错误');
}
// 成功提示(可选)
success(message: string): void {
this.toastr.success(message, '成功');
}
}
三、高级扩展技巧
1. 排除特定请求
某些请求(如登录、注册)不需要添加 Token,可在拦截器中排除:
private addTokenToRequest(request: HttpRequest<unknown>): HttpRequest<unknown> {
// 排除登录接口
const excludedUrls = ['/api/auth/login', '/api/auth/register'];
if (excludedUrls.some(url => request.url.includes(url))) {
return request;
}
const token = localStorage.getItem('auth_token');
if (token) {
return request.clone({
setHeaders: {
'Authorization': `Bearer ${token}`
}
});
}
return request;
}
2. 多个拦截器的执行顺序
当注册多个拦截器时,providers数组中的顺序即为拦截器的执行顺序:
- 请求阶段:按数组顺序执行
- 响应阶段:按数组逆序执行
例如:
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: InterceptorA, multi: true }, // 先执行
{ provide: HTTP_INTERCEPTORS, useClass: InterceptorB, multi: true } // 后执行
]
请求时:A → B → 发送请求响应时:B → A → 组件
四、注意事项
- 请求对象不可变:
HttpRequest是不可变对象,必须通过clone()方法修改后再返回。 - 避免无限循环:拦截器中如果发起新的 HTTP 请求,务必排除该请求,否则会导致无限拦截。
- Token 安全存储:示例中使用
localStorage存储 Token,生产环境建议结合HttpOnly Cookie或加密存储。 - 错误处理粒度:全局拦截器处理通用错误,特定请求的个性化错误仍需在订阅处单独处理。
总结
- Angular HTTP 拦截器通过实现
HttpInterceptor接口,可统一拦截请求和响应,避免重复代码。 - 请求拦截核心是克隆
HttpRequest并添加 Token 头,响应拦截通过catchError统一处理不同状态码的错误。 - 拦截器需在根模块注册,
multi: true允许注册多个拦截器,执行顺序需根据业务需求合理设置。
通过本文的实现方案,你可以快速为 Angular 项目搭建起规范的 HTTP 请求处理体系,提升代码可维护性和用户体验。

89

被折叠的 条评论
为什么被折叠?



