使用@CurrentUser自定义装饰器时Joi校验异常触发请求头校验问题
问题:使用@CurrentUser装饰器后Joi校验出现"user_id不允许"错误
问题场景
我在NestJS项目中编写了如下代码,当不使用@CurrentUser()装饰器时,payload打印正常;但使用该装饰器后,出现Joi校验错误,提示"user_id" is not allowed。
核心代码
export const CurrentUser = createParamDecorator( (data: unknown, ctx: ExecutionContext) => { const request = ctx.switchToHttp().getRequest(); return request.user ?? null; }, ); @Injectable() export class JoiValidationPipe implements PipeTransform { constructor(private schema: ObjectSchema) {} transform(value: any) { const { error } = this.schema.validate(value); if (error) { throw new BadRequestException('Request validation failed: ' + error); } return value; } } const createPostJoiSchema = Joi.object().keys({ content: Joi.string() .min(10) .message('Post cannot be too short.') .disallow(''), title: Joi.string().max(100).message('Max 100 characters').disallow(''), vote_count: Joi.number().min(1).max(1), tags: Joi.array().items(Joi.string().max(30).disallow('')).min(1).max(10), }); export const createPostValidationPipe = new JoiValidationPipe( createPostJoiSchema, ); @Post() @UseGuards(AuthGuardJwt) @UsePipes(createPostValidationPipe) async create(@Body() payload: any, @CurrentUser() currentUser) { console.log('post payload: ', payload); return; }
错误信息
{ "message": "Request validation failed: ValidationError: \"user_id\" is not allowed", "error": "Bad Request", "statusCode": 400 }
JwtStrategy实现
@Injectable() export class JwtStrategy extends PassportStrategy( Strategy, authStrategies.jwtStrategy, ) { constructor(private readonly userService: UserService) { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, secretOrKey: authConfigs.authSecret, }); } public async validate(payload: any) { const userProfile = await this.userService.findOne({ user_id: payload.sub, }); return replacePasswordInNewObject(userProfile); } }
原因分析
问题出在自定义的JoiValidationPipe上:它没有指定校验的参数类型,默认会对整个请求对象进行校验,而非仅校验@Body()的内容。
当添加@CurrentUser()装饰器后,NestJS的参数解析逻辑会触发Pipe对整个request对象做校验——此时request上的user属性(由JwtStrategy的validate方法返回,包含user_id字段)会被Joi schema检测到,但你的createPostJoiSchema并未定义user_id字段,因此触发了"不允许存在user_id"的校验错误。
而不使用@CurrentUser()时,NestJS默认仅将@Body()的内容传给Pipe,所以不会触发该问题。
解决方法
方法1:限定Pipe仅校验Body参数
在控制器的@UsePipes()中指定Pipe作用范围为Body:
// 先导入ParamType import { ParamType } from '@nestjs/common'; @Post() @UseGuards(AuthGuardJwt) @UsePipes({ value: createPostValidationPipe, // 关键:限定Pipe仅作用于Body参数 type: ParamType.BODY }) async create(@Body() payload: any, @CurrentUser() currentUser) { console.log('post payload: ', payload); return; }
方法2:修改JoiValidationPipe逻辑(更稳妥)
确保Pipe只处理传入的单个参数值,而非默认的整个request对象。可以在transform方法中增加判断,或者改用NestJS内置的ValidationPipe配合class-validator(推荐长期使用)。
方法3:允许Joi schema接受未知字段(不推荐)
如果不想限定Pipe范围,可在schema中添加unknown(true)绕过额外字段校验,但可能隐藏其他参数错误,不建议生产环境使用:
const createPostJoiSchema = Joi.object().keys({ // 原有字段定义... }).unknown(true);
内容的提问来源于stack exchange,提问作者juztcode
相关产品推荐
相关产品推荐

