nestjs学习 - 拦截器(intercept)
拦截器是使用 @Injectable() 装饰器注解的类。拦截器应该实现 NestInterceptor 接口。
![]()
一、它是什么
拦截器(Interceptor) 是一个基于 面向切面编程(AOP) 思想的强大功能。它允许你在请求到达控制器(Controller)之前或之后,以及响应返回给客户端之前,插入自定义逻辑。
白话:
从上图可以看出,拦截器就是可以在
到达请求前和请求返回结果后进行拦截,做一些你想做的事情;前端开发同学可以结合 axios 的拦截器理解,几乎就是同一个模式;
简单说:拦截器就是请求和响应路上的“把关人”,能在不修改核心业务代码的情况下,统一处理一些公共逻辑。
在下文中主要关注它的使用场景;
在框架生命周期中,它的执行时机是:
请求进入 → 中间件 → 守卫 →
拦截器→ 管道 → 控制器 → 服务 →拦截器→ 异常过滤器 → 服务器响应
二、使用方法
在使用拦截器之前,需了解 RxJS(响应式编程库) 的使用
它底层严重依赖 RxJS,因为 intercept() 方法返回的是一个 Observable(可观察对象)。这意味着你需要对 RxJS 的操作符(如 map, tap, catchError 等)有一定了解。
1. 创建
统一响应数据格式demo:
import { NestInterceptor, CallHandler, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
interface Data<T> {
code: number;
message: string;
data: T;
}
/**
* 响应拦截器
* 用于处理响应数据
* 可以用于处理响应数据,如添加响应头,添加响应体等
*/
@Injectable()
export class ResponseInterceptor<T> implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<Data<T>> {
// ==========================
// 【阶段 1:控制器执行之前】
// ==========================
// 这里的代码会立即同步执行。
// 此时请求刚到达拦截器,还没进控制器。
console.log('❤️ [之前] 请求已到达拦截器');
const startTime = Date.now();
// 可以在这里做:权限预检、记录开始时间、修改请求参数等。
// 如果在这里直接 return 一个 Observable (例如 return of({error: 'blocked'}))
// 而不调用 next.handle(),控制器将永远不会执行(短路)。
// 调用 next.handle() 启动控制器逻辑
// 它返回一个 Observable,代表控制器未来的执行结果(流)
const response$ = next.handle();
// ==========================
// 【阶段 2:控制器执行之后】
// ==========================
// 这里的代码不会立即执行!
// 它们被注册为 RxJS 的“操作符”,只有当控制器执行完毕并产生数据时,流才会流动到这里。
return response$.pipe(
map(data => {
return {
code: 200,
message: 'success',
data,
};
}),
);
}
}
2. 注册
有三种注册方式,作用范围依次扩大:
方法级别:
仅针对某个特定路由
@Get('users')
@UseInterceptors(LoggingInterceptor)
findAll() {
return this.userService.findAll();
}
控制器级别:
针对该控制器下的所有路由
@Controller('users')
@UseInterceptors(LoggingInterceptor)
export class UsersController {
// ...
}
全局级别:
针对整个应用的所有路由,在 main.ts 中注册:
const app = await NestFactory.create(AppModule);
app.useGlobalInterceptors(new LoggingInterceptor());
await app.listen(3000);
三、使用场景:
-
统一响应格式格式化(正常数据、错误数据)
-
响应缓存
对于不经常变动的数据(如配置信息、列表页),可以在拦截器中检查缓存。
- 如果缓存命中,直接
return of(cachedData),不调用next.handle(),从而跳过控制器逻辑,极大提升性能。 - 如果未命中,正常执行并写入缓存。
- 如果缓存命中,直接
-
超时处理
如果某个请求处理时间过长,可以强制中断。
import { timeout } from 'rxjs/operators'; // 在 intercept 方法中 return next.handle().pipe( timeout(5000), // 5秒无响应则抛出异常 ); -
数据序列化/脱敏
在返回给用户之前,动态修改敏感字段。
- 例如:将用户列表中的
password字段移除,或将手机号中间四位替换为****。 - 通过
map操作符遍历返回数据并进行清洗。
- 例如:将用户列表中的
四、总结:
- 基于 AOP 思想,利用 RxJS 在请求/响应生命周期中插入逻辑的机制。
-
它本质上是一个强大的“切面”工具,用于处理那些横跨整个应用程序的、与核心业务逻辑无关的公共关注点。
它的精髓在于:你可以在不侵入、不修改任何一个现有控制器方法的情况下,为整个应用或特定接口批量添加上述各种功能。 这使得你的代码更加干净、可维护,并且这些横切关注点可以被轻松地复用和组合。
- 统一返回格式、日志记录、性能监控、缓存、数据转换、超时控制。
- 区别: 比中间件更灵活,能操作返回值;比守卫(Guard)更侧重于数据转换而非权限决策。