Note
拦截器
nestjs的拦截器介绍
发布于 更新于
拦截器
包裹在控制器方法外面的一层处理程序
作用
它不仅能在控制器执行前做事情,还能在控制器执行后继续处理返回值、异常和整个请求流程
定义
使用 @Injectable() 修饰,并实现 NestInterceptor 接口,那么这个类就是拦截器,拦截器内部必须实现 intercept方法。拦截器文件通常命名为 xxx.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from "@nestjs/common";
import { Observable } from "rxjs";
@Injectable()
export class TestInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
console.log("控制器执行前-前置代码");
return next.handle().pipe(
// data 响应数据
map((data) => {
console.log("控制器执行后-后置代码");
return data;
})
);
}
}
// 执行顺序:
// 前置代码
// ↓
// 控制器执行
// ↓
// 后置代码
参数CallHandler
它有一个非常重要的方法handle,这个方法表示继续执行后续流程(包括管道、控制器方法以及控制器内部调用的服务),如果拦截器不调用 next.handle() ,控制器方法就不会执行。并且 next.handle() 会返回 Observalbe ,这属于 rxjs 概念。
Rxjs常用操作符
- map():转换控制器返回结果,将每个原始值转换成新值
控制器原本的返回值
[
{
"id": 1,
"name": "张三"
}
]
map修改返回值
return next.handle().pipe(
map((data) => {
return {
code: 200,
data,
};
})
);
最终返回
{
"code": 200,
"data": [
{
"id": 1,
"name": "张三"
}
]
}
- tap():用于观察数据,但不修改数据。因为它主要起副作用,例如:日志、性能统计、埋点、事件记录等等
return next.handle().pipe(
tap((data) => {
console.log("控制器返回了:", data);
})
);
- finalize():在请求结束时执行,不管是成功还是异常。常用于计算总耗时、释放资源、结束日志追踪、关闭临时状态等等
return next.handle().pipe(
finalize(() => {
console.log("请求处理结束");
})
);
- catchError():用于捕获异常,接收到控制器、服务或者管道中产生的异常。如果还想让异常继续交给异常过滤器处理,需要重新抛出
return next.handle().pipe(
catchError((error) => {
console.error("请求出错:", error);
// 让异常继续交给异常过滤器处理
return throwError(() => error);
})
);
- timeout():设置超时,可以设置在请求超时后抛出异常
return next.handle().pipe(
// 设置超时时间
timeout(5000),
catchError((error) => {
console.error("请求超时:", error);
return throwError(() => error);
})
);
- of():创建一个新的Observable。用于直接返回结果,常用于缓存。
@Injectable()
export class SimpleCacheInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler) {
const cacheData = {
id: 1,
name: "缓存用户",
};
const hasCache = true;
if (hasCache) {
return of(cacheData);
}
return next.handle();
}
}
执行逻辑
检查缓存
↓
有缓存?
┌──┴──┐
是 否
↓ ↓
of() next.handle()
↓ ↓
直接 执行控制器
返回
注册/作用域
方法级
只作用于对应方法
@Controller("users")
export class UsersController {
@Get()
@UseInterceptors(LoggingInterceptor)
findAll() {
return [];
}
}
控制器级
只作用于对应控制器
@Controller("users")
@UseInterceptors(LoggingInterceptor)
export class UsersController {
@Get()
findAll() {
return [];
}
@Post()
create() {
return {};
}
}
全局
作用于整个应用中的所有控制器和路由。也可通过在任意模块中使用 APP_INTERCEPTOR + useClass 进行全局注册。
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 注册全局拦截器
app.useGlobalInterceptors(new LoggingInterceptor());
await app.listen(3000);
}
多个拦截器执行顺序
拦截器进入阶段依次按照全局、控制器、路由运行,返回阶段则按照路由、控制器、全局的顺序反向解析,即先进后出
假设有三个拦截器:
GlobalInterceptor
ControllerInterceptor
RouteInterceptor
执行顺序为:
Global 前置
↓
Controller 前置
↓
Route 前置
↓
Controller 方法
Controller 方法返回
↓
Route 后置
↓
Controller 后置
↓
Global 后置