You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 11:12:53