拦截器 (Interceptors)
拦截器可包装响应、记录日志、映射 DTO。见 guards。
拦截器是 NestJS 中一种强大的 AOP(面向切面编程)组件,可以在路由处理器的执行前后插入自定义逻辑。它们基于 RxJS 的 Observable 对象,使得你可以灵活地操纵请求-响应流。
一、拦截器的作用
| 功能 | 描述 |
|---|---|
| 方法执行前后逻辑 | 在控制器方法执行前/后执行额外代码(如日志、计时、事务开启/提交) |
| 转换返回结果 | 统一包装响应格式、自动序列化、过滤敏感字段 |
| 扩展功能 | 实现缓存、响应的性能监测、请求/响应脱敏 |
| 处理超时或流式响应 | 设置超时时间,或者对流式响应进行中间处理 |
| 异常映射 | 捕获异常并转换为友好格式(通常配合异常过滤器) |
二、拦截器接口与切面模式
拦截器实现 NestInterceptor 接口,其中 intercept(context: ExecutionContext, next: CallHandler) 方法返回 Observable<any>。
context:执行上下文,类似于守卫中的ExecutionContext,可以获取请求对象等。next:CallHandler对象,它的handle()方法返回一个 RxJS Observable,代表原始控制器处理器的响应流。
通过 RxJS 操作符(如 map、tap、catchError)可以修改响应或处理副作用。
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from "@nestjs/common";
import { Observable } from "rxjs";
import { map } from "rxjs/operators";
@Injectable()
export class TransformInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
// 请求前逻辑(例如计时开始)
console.log("Before controller...");
return next.handle().pipe(
// 请求后逻辑(修改响应)
map((data) => ({ data, code: 0, message: "success" })),
);
}
}
常用的 RxJS 操作符:
| 操作符 | 作用 |
|---|---|
map | 修改响应数据(如包装格式) |
tap | 执行副作用(如日志记录),不改变响应 |
catchError | 捕获错误,转换为自定义错误响应 |
timeout | 设置超时,抛出超时错误 |
三、响应映射示例:统一包装格式
很多 API 会返回固定格式 { code, data, message },可以使用全局响应拦截器统一实现。
// common/interceptors/response.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from "@nestjs/common";
import { Observable } from "rxjs";
import { map } from "rxjs/operators";
export interface Response<T> {
code: number;
data: T;
message: string;
}
@Injectable()
export class ResponseInterceptor<T> implements NestInterceptor<T, Response<T>> {
intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> {
return next.handle().pipe(
map((data) => ({
code: 0,
data,
message: "success",
})),
);
}
}
然后在控制器方法中正常返回数据即可,拦截器会自动包装。
四、使用拦截器
拦截器可以通过 @UseInterceptors() 装饰器应用在方法、控制器或全局范围内。
1. 方法级
@Post()
@UseInterceptors(TransformInterceptor)
create(@Body() dto: CreateUserDto) {
return this.userService.create(dto);
}
2. 控制器级
@Controller('users')
@UseInterceptors(LoggingInterceptor)
export class UsersController { ... }
3. 全局级(无依赖注入)
// main.ts
app.useGlobalInterceptors(new ResponseInterceptor());
4. 全局级(支持依赖注入)
使用 APP_INTERCEPTOR 令牌在模块中注册,这样拦截器就可以注入其他服务。
import { Module } from "@nestjs/common";
import { APP_INTERCEPTOR } from "@nestjs/core";
import { ResponseInterceptor } from "./common/interceptors/response.interceptor";
@Module({
providers: [
{
provide: APP_INTERCEPTOR,
useClass: ResponseInterceptor,
},
],
})
export class AppModule {}
五、内置拦截器:ClassSerializerInterceptor
@nestjs/common 提供了内置的 ClassSerializerInterceptor,它利用 class-transformer 的 @Exclude()、@Expose()、@SerializeOptions() 等装饰器来控制对象序列化,常用于隐藏敏感字段(如密码)。
使用步骤
- 安装依赖(通常已有):
npm install class-transformer
- 在 DTO 或实体中使用
@Exclude()装饰器:
import { Exclude } from "class-transformer";
export class UserEntity {
id: number;
name: string;
@Exclude()
password: string;
}
- 在控制器方法上应用
ClassSerializerInterceptor:
@Get(':id')
@UseInterceptors(ClassSerializerInterceptor)
findOne(@Param('id') id: number): UserEntity {
// 返回 user 实例,password 字段会被自动排除
return this.userService.findOne(id);
}
也可以全局注册,让所有返回的实体都自动应用序列化规则。
六、高级用例:超时处理
使用 timeout 操作符设置请求最大处理时间:
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
RequestTimeoutException,
} from "@nestjs/common";
import { Observable, throwError } from "rxjs";
import { timeout, catchError } from "rxjs/operators";
@Injectable()
export class TimeoutInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
timeout(5000), // 5 秒超时
catchError((err) => {
if (err.name === "TimeoutError") {
return throwError(() => new RequestTimeoutException());
}
return throwError(() => err);
}),
);
}
}
七、拦截器的执行顺序
多个拦截器可以同时应用,执行顺序为:
- 请求前:按照注册顺序执行(最外层 → 内层)
- 控制器方法执行
- 响应后:按照注册顺序的逆序执行(内层 → 最外层)
对于全局拦截器,会先于控制器/方法级拦截器执行(在 NestJS 中,全局拦截器始终在最外层)。
八、与管道的区别
| 组件 | 主要职责 | 执行时机 | 操作对象 |
|---|---|---|---|
| 管道 | 验证和转换输入数据 | 控制器方法执行前(参数层面) | 请求参数(@Body()、@Query() 等) |
| 拦截器 | 修改输出结果、处理响应流 | 控制器方法执行前后 | 响应对象(整个 Observable 流) |
总结
| 概念 | 说明 |
|---|---|
| 拦截器接口 | 实现 NestInterceptor,intercept 方法 + next.handle() |
| 切面模式 | 使用 RxJS 操作符(map、tap、catchError)处理响应流 |
| 响应映射 | 统一包装数据结构,简化控制器 |
| 使用方式 | @UseInterceptors() 方法/控制器/全局(app.useGlobalInterceptors 或 APP_INTERCEPTOR) |
| 内置拦截器 | ClassSerializerInterceptor 自动排除标记字段(配合 @Exclude()) |
| 高级功能 | 超时控制、缓存拦截、日志记录、性能统计 |
掌握拦截器,你可以将横切关注点(cross-cutting concerns)从业务逻辑中抽离出来,实现更干净、可复用的代码。
参考文献
以下链接在编写时均可正常访问:
| 资料 | 说明 |
|---|---|
| NestJS 文档 | 官方 |
| Interceptors | 本章主题 |
| Request lifecycle | 执行顺序 |
相关文章
认证与授权
认证(Authentication):确认「你是谁」(如 JWT、Session)。 - 授权(Authorization):确认「你能做什么」(如 RBAC、策略检查)。
缓存
@nestjs/cache-manager 统一缓存 API,存储实现可插拔。
提供者与服务
Service 是最常见的 Provider,封装业务逻辑。见 module。
守卫 (Guards) 与授权
守卫决定是否放行请求,常用于认证与授权。见 auth。
配置管理(Config 模块)
@nestjs/config 加载 .env 并提供 ConfigService。见 工程化 env。
数据库集成(以 TypeORM 为例)
Nest 通过 @nestjs/typeorm 等包集成 ORM;生产环境用 migration,慎用 synchronize。亦可选用 Prisma、MikroORM 等(见 官方 Database)。
Series
new
12 / 19