如何在Nest.js拦截器中访问上下文类的非静态服务属性?
解决NestJS拦截器中访问控制器关联服务的问题
你遇到的核心问题是:context.getClass() 获取到的是控制器的类构造函数,而非运行时的实例对象,所以只能访问静态成员,无法直接获取类中注入的非静态服务。下面提供两种可行的解决方案:
方案一:通过ModuleRef获取控制器实例
利用你已经注入的ModuleRef,可以获取到当前请求对应的控制器实例,进而访问实例中的服务依赖:
async intercept( context: ExecutionContext, next: CallHandler, ): Promise<Observable<any>> { const req = context.switchToHttp().getRequest(); // 获取控制器类 const controllerClass = context.getClass(); // 获取控制器实例(支持单例/请求作用域) const controllerInstance = await this.moduleRef.resolve(controllerClass); // 假设控制器中注入了名为documentService的服务 const targetService = controllerInstance.documentService; // 根据请求参数获取修改前的数据 const originalData = await targetService.findOne(req.params.id); return next.handle().pipe( tap(async (responseData) => { // 结合原始数据与响应数据生成审计日志 await this.auditlogService.create({ method: req.method, originalData, modifiedData: responseData, // 补充其他审计字段 }); }), ); }
方案二:用元数据标记目标服务(更灵活)
如果不同控制器/方法需要调用不同的服务,可以自定义装饰器标记需要使用的服务,再通过Reflector读取元数据:
1. 定义自定义装饰器
import { SetMetadata } from '@nestjs/common'; export const AuditTargetService = (service: any) => SetMetadata('audit-target-service', service);
2. 在控制器方法上标记
@Put(':id') @AuditTargetService(DocumentService) async update(@Param('id') id: string, @Body() data: UpdateDto) { return this.documentService.update(id, data); }
3. 在拦截器中获取并使用服务
async intercept( context: ExecutionContext, next: CallHandler, ): Promise<Observable<any>> { const req = context.switchToHttp().getRequest(); // 读取元数据中的目标服务类型 const targetServiceClass = this.reflector.get<any>( 'audit-target-service', context.getHandler(), ); // 获取服务实例 const targetService = await this.moduleRef.resolve(targetServiceClass); const originalData = await targetService.findOne(req.params.id); return next.handle().pipe( tap(async (responseData) => { await this.auditlogService.create({ method: req.method, originalData, modifiedData: responseData, }); }), ); }
注意事项
- 如果你的服务是请求作用域的,必须使用
moduleRef.resolve()而非moduleRef.get(),前者会创建/获取当前请求上下文的实例。 - 拦截器的
tap操作符中使用async时,NestJS不会等待Promise完成,若需要确保审计日志写入完成后再返回响应,可以改用switchMap或mergeMap:return next.handle().pipe( switchMap(async (responseData) => { await this.auditlogService.create(/* 审计日志参数 */); return responseData; }), );
内容的提问来源于stack exchange,提问作者Afsal Kp
相关产品推荐
相关产品推荐

