如何通过自定义注解向NestJS执行上下文添加指定值
需求可实现,通过「自定义装饰器+反射+拦截器」组合即可完成你要的效果
你之前遇到的两个问题本质是对Nest的运行机制理解有偏差:
- 装饰器属于静态元编程能力,仅在类/方法加载阶段执行,此时请求还未发起,自然拿不到运行时才生成的ExecutionContext
- 拦截器要接收自定义参数,需要通过装饰器先把参数存入路由元数据,再在拦截器中通过反射读取即可
步骤1:实现自定义装饰器
使用Nest内置的SetMetadata工具方法,把你要传递的自定义值存入路由方法的元数据中:
import { SetMetadata } from '@nestjs/common'; // 定义元数据的key,避免硬编码冲突 export const MY_CUSTOM_CONTEXT_KEY = 'my_custom_context_key'; // 导出你要的@MyAnnotation装饰器 export const MyAnnotation = (customValue: string) => SetMetadata(MY_CUSTOM_CONTEXT_KEY, customValue);
步骤2:实现拦截器读取元数据并写入上下文
在拦截器中注入Reflector工具,读取刚才存入的元数据,再写入当前请求的上下文即可:
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { Observable } from 'rxjs'; import { MY_CUSTOM_CONTEXT_KEY } from './my-annotation.decorator'; @Injectable() export class CustomContextInterceptor implements NestInterceptor { constructor(private reflector: Reflector) {} intercept(context: ExecutionContext, next: CallHandler): Observable<any> { // 读取当前路由方法上的自定义元数据 const customValue = this.reflector.get<string>( MY_CUSTOM_CONTEXT_KEY, context.getHandler() ); // 只有加了@MyAnnotation装饰器的方法才会进入逻辑 if (customValue) { // 以HTTP上下文为例,拿到request对象,把自定义值挂载上去 // 如果是RPC/WebSocket上下文,调用对应context.switchToXxx()方法即可 const request = context.switchToHttp().getRequest(); // 写入自定义值到请求上下文,后续守卫、管道、控制器方法都可以读取该值 request.myCustomContextValue = customValue; } return next.handle(); } }
步骤3:注册拦截器并使用
首先注册拦截器,推荐全局注册,不用每个控制器单独加:
// 在根模块的providers中添加如下配置 import { APP_INTERCEPTOR } from '@nestjs/core'; import { CustomContextInterceptor } from './custom-context.interceptor'; @Module({ providers: [ { provide: APP_INTERCEPTOR, useClass: CustomContextInterceptor, }, ], }) export class AppModule {}
之后就可以直接在控制器方法上按你想要的方式使用了:
@Controller('docs') export class MyController { constructor(private readonly service: SubjectsService) {} @Get('/:Id') @MyAnnotation('addThisStringToContext') async find(@Param() id: string, @Req() request: Request) { // 直接从请求上下文拿到你写入的自定义值 console.log(request.myCustomContextValue); // 输出:addThisStringToContext return await this.service.find(id); } }
内容的提问来源于stack exchange,提问作者Alon Mota
相关产品推荐
相关产品推荐

