NestJS自定义装饰器中如何注入OtpClient服务?
在NestJS中实现带依赖注入的OTP验证参数装饰器
NestJS的参数装饰器默认无法直接注入依赖,因为它运行在DI容器生命周期之外。你可以通过以下两种方式解决这个问题,推荐使用第一种符合框架设计的方案。
方案一:自定义守卫 + 参数装饰器(推荐)
守卫是NestJS中处理路由前置验证的标准方式,支持依赖注入,我们可以先在守卫中完成OTP验证,再把结果附加到请求对象,最后用参数装饰器提取结果。
1. 创建OTP验证守卫
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common'; import { OtpClient } from '../../otp/otp.client'; @Injectable() export class OtpValidationGuard implements CanActivate { constructor(private readonly otpClient: OtpClient) {} async canActivate(context: ExecutionContext): Promise<boolean> { const request = context.switchToHttp().getRequest(); const { otp } = request.body; if (!otp) { throw new UnauthorizedException('OTP参数缺失'); } try { // 调用OtpClient的验证方法 const validationResult = await this.otpClient.validateOtp(otp); // 将验证结果挂载到request对象,供后续使用 request.otpValidationResult = validationResult; return true; } catch (error) { throw new UnauthorizedException('OTP验证失败'); } } }
2. 创建参数装饰器提取验证结果
import { createParamDecorator, ExecutionContext } from '@nestjs/common'; export const OtpValidation = createParamDecorator( (_: unknown, ctx: ExecutionContext) => { const request = ctx.switchToHttp().getRequest(); // 提取之前守卫存入的验证结果 return request.otpValidationResult; }, );
3. 在路由中使用
import { Controller, Post, UseGuards } from '@nestjs/common'; import { OtpValidationGuard } from './otp-validation.guard'; import { OtpValidation } from './otp-validation.decorator'; import { ValidateOtpTokenResponseDTO } from './dto/validate-otp.dto'; @Controller('auth') export class AuthController { @Post('verify-otp') // 启用OTP验证守卫 @UseGuards(OtpValidationGuard) async verifyOtp( // 通过装饰器获取验证后的结果 @OtpValidation() validationResult: ValidateOtpTokenResponseDTO ) { return { success: true, data: validationResult }; } }
方案二:直接在参数装饰器中获取ModuleRef(不推荐)
如果你坚持要在参数装饰器中直接获取OtpClient,可以通过ModuleRef实例来获取,但这种方式依赖NestJS内部参数顺序,可能在版本更新时失效。
修改后的装饰器代码
import { createParamDecorator, ExecutionContext, ModuleRef } from '@nestjs/common'; import { OtpClient } from '../../otp/otp.client'; export const ValidateOtpDecorator = createParamDecorator( async (_: unknown, ctx: ExecutionContext) => { const request = ctx.switchToHttp().getRequest(); const { otp } = request.body; // 获取ModuleRef实例(HTTP上下文下,参数索引为3,不同上下文可能不同) const moduleRef = ctx.getArgs()[3] as ModuleRef; // 获取OtpClient实例 const otpClient = moduleRef.get(OtpClient); return await otpClient.validateOtp(otp); }, );
你之前尝试失败的原因
你之前用moduleRef.get(OTP_CLIENT_ADAPTER)失败,大概率是因为没有在模块中正确注册OTP_CLIENT_ADAPTER令牌对应的提供者。如果OtpClient实现了OtpClientAdapter接口,需要在模块中注册:
@Module({ providers: [ { provide: OTP_CLIENT_ADAPTER, // 注入令牌 useClass: OtpClient, // 对应的实现类 }, OtpClient, // 其他依赖(比如LoggerAdapter、HttpInterceptor) ], }) export class OtpModule {}
内容的提问来源于stack exchange,提问作者Vinícius Britto
相关产品推荐
相关产品推荐

