NestJS 拦截器概述
拦截器(Interceptor)是 NestJS 的核心功能之一,用于在方法执行前后添加额外的逻辑。拦截器基于面向切面编程(AOP)思想,常用于日志记录、性能监控、响应格式统一等场景。
拦截器的核心功能
- 方法执行前/后注入逻辑:可以在目标方法执行前后插入自定义代码。
- 修改请求/响应数据:拦截请求或响应的数据流并对其进行转换。
- 异常处理:捕获并处理目标方法抛出的异常。
- 异步拦截:支持
Observable和Promise的异步操作。
创建拦截器
拦截器是一个实现了NestInterceptor接口的类,需使用@Injectable()装饰器。核心方法是intercept,接收两个参数:
context: ExecutionContext:提供当前请求的上下文信息。next: CallHandler:调用目标方法的控制器逻辑。
import{CallHandler,ExecutionContext,Injectable,NestInterceptor}from'@nestjs/common';import{map,Observable}from'rxjs';@Injectable()exportclassSerializeInterceptorimplementsNestInterceptor{intercept(context:ExecutionContext,next:CallHandler):Observable<any>{console.log('拦截器执行之前')constreq=context.switchToHttp().getRequest();console.log('请求对象',req)returnnext.handle().pipe(map((data)=>{console.log('拦截器执行之后')console.log('数据',data)returndata}));}}注册拦截器
拦截器可以全局注册、模块注册或方法级注册:
全局注册
在main.ts中使用SerializeInterceptor:
app.useGlobalInterceptors(newSerializeInterceptor());模块注册
通过模块的providers数组注册,并标记为可注入:
@Module({providers:[{provide:APP_INTERCEPTOR,useClass:SerializeInterceptor,},],})exportclassAppModule{}方法级注册
直接在控制器方法上使用@UseInterceptors装饰器:
@Controller('users')exportclassUsersController{@UseInterceptors(SerializeInterceptor)@Get()findAll(){/* ... */}}常见应用场景
统一响应格式
通过拦截器将控制器返回的数据包装为固定格式:
@Injectable()exportclassTransformInterceptorimplementsNestInterceptor{intercept(context:ExecutionContext,next:CallHandler):Observable<any>{returnnext.handle().pipe(map(data=>({success:true,data})));}}性能监控
记录方法执行耗时:
@Injectable()exportclassTimingInterceptorimplementsNestInterceptor{intercept(context:ExecutionContext,next:CallHandler):Observable<any>{conststart=Date.now();returnnext.handle().pipe(tap(()=>console.log(`Execution time:${Date.now()-start}ms`)),);}}缓存拦截
实现简单的缓存逻辑:
@Injectable()exportclassCacheInterceptorimplementsNestInterceptor{privatecache=newMap<string,any>();intercept(context:ExecutionContext,next:CallHandler):Observable<any>{constrequest=context.switchToHttp().getRequest();constkey=request.url;if(this.cache.has(key)){returnof(this.cache.get(key));}returnnext.handle().pipe(tap((data)=>this.cache.set(key,data)),);}}高级用法
修改请求数据
通过拦截器预处理请求参数:
@Injectable()exportclassSanitizeInterceptorimplementsNestInterceptor{intercept(context:ExecutionContext,next:CallHandler):Observable<any>{constrequest=context.switchToHttp().getRequest();request.body=this.sanitize(request.body);returnnext.handle();}privatesanitize(data:any){// 实现数据清洗逻辑returndata;}}异步拦截
结合rxjs操作符处理异步流:
@Injectable()exportclassTimeoutInterceptorimplementsNestInterceptor{intercept(context:ExecutionContext,next:CallHandler):Observable<any>{returnnext.handle().pipe(timeout(5000),// 5秒超时catchError((err)=>throwError(newRequestTimeoutException())),);}}小栗子
实现请求敏感信息的过滤
import{CallHandler,ExecutionContext,Injectable,NestInterceptor}from'@nestjs/common';import{plainToInstance}from'class-transformer';import{map,Observable}from'rxjs';@Injectable()exportclassSerializeInterceptorimplementsNestInterceptor{constructor(privatedto?:any){}intercept(context:ExecutionContext,next:CallHandler):Observable<any>{console.log('拦截器执行之前')constreq=context.switchToHttp().getRequest();console.log('请求对象',req)returnnext.handle().pipe(map((data)=>{console.log('拦截器执行之后')console.log('数据',data)returnplainToInstance(this.dto,data,{// Expose 设置暴露字段// Exclude 设置排除字段excludeExtraneousValues:true,// 设置为true之后,所有经过拦截器的接口都需要设置Expose或者Exclude})}));}}拦截器与过滤器的区别
- 拦截器:作用于方法调用前后,可修改请求/响应数据流。
- 异常过滤器:仅捕获异常,无法修改正常流程的数据。
最佳实践
- 避免在拦截器中实现业务逻辑,保持职责单一。
- 优先使用模块注册而非全局注册,便于测试和依赖管理。
- 对于性能敏感的拦截器,可通过缓存减少重复计算。