NestJS中AuthGuard调用SetMetadata设置的元数据在拦截器中获取不到如何解决
问题原因
SetMetadata 是 Nest 提供的装饰器工厂函数,仅在**代码加载阶段(类/方法初始化时)**生效,作用是给目标类、方法、参数挂载静态元数据。你在 Guard 的 canActivate 方法(请求运行时)调用SetMetadata,只是生成了一个未绑定到任何目标的装饰器实例,不会修改已经初始化完成的路由方法的元数据,所以拦截器中无法读取到对应值。
另外要注意:路由方法本身是单例的,就算你强行修改路由方法的运行时元数据,并发请求也会出现值覆盖的问题,导致日志记录错误,绝对不要用修改静态元数据的方式存储请求维度的动态值。
解决方案
直接将动态值挂载到当前请求的 request 对象上即可,同一个请求的 Guard、拦截器、控制器会共享同一个 request 实例,不会出现并发冲突问题。
1. 修改 JwtAuthGuard 代码
@Injectable() export class JwtAuthGuard extends AuthGuard("jwt") { constructor( private readonly reflector: Reflector, private readonly someService: SomeService ) { super(); } public async canActivate(context: ExecutionContext): Promise<boolean> { const role = this.reflector.get<string>("permission", context.getHandler()); const request = context.switchToHttp().getRequest(); const { valueToLog } = request.body; const jwtToken = request.headers.authorization; console.log("check value exist", valueToLog); // 将动态值挂载到request对象 request.valueToLog = valueToLog; // 补上原有鉴权逻辑,这里示例直接调用父类方法 return super.canActivate(context) as Promise<boolean>; } }
2. 修改 LoggingInterceptor 代码
@Injectable() export default class LoggingInterceptor { constructor(private readonly reflector: Reflector) {} intercept(context: ExecutionContext, next: CallHandler) { const executionStart = Date.now(); // 先取当前请求的request对象 const request = context.switchToHttp().getRequest(); return next.handle().pipe( tap((responseData) => { const response = context.switchToHttp().getResponse<ServerResponse>(); const { statusCode } = response; // 直接从request中读取动态值 const valueToLog = request.valueToLog; const endpoint = this.reflector.get("endpoint", context.getHandler()); const functionalMap = this.reflector.get("functionalMap", context.getHandler()); // 原有日志逻辑 }) ); } }
可选:TypeScript 类型扩展
如果使用 TypeScript ,可以扩展 Express 的 Request 类型避免类型报错,在项目的类型声明文件(如typings/express/index.d.ts)中添加以下内容:
declare namespace Express { interface Request { // 替换为你实际的valueToLog类型 valueToLog: string; } }
内容的提问来源于stack exchange,提问作者LuxuryWaffles
相关产品推荐
相关产品推荐

