【问题标题】:Validate nested objects using class validator and nestjs使用类验证器和 nestjs 验证嵌套对象
【发布时间】:2019-05-16 02:49:21
【问题描述】:

我正在尝试使用类验证器和 NestJS 验证嵌套对象。我已经尝试通过使用 class-transform 中的 @Type 装饰器来关注这个 thread 并且没有任何运气。这是我所拥有的:

DTO:

class PositionDto {
  @IsNumber()
  cost: number;

  @IsNumber()
  quantity: number;
}

export class FreeAgentsCreateEventDto {

  @IsNumber()
  eventId: number;

  @IsEnum(FinderGamesSkillLevel)
  skillLevel: FinderGamesSkillLevel;

  @ValidateNested({ each: true })
  @Type(() => PositionDto)
  positions: PositionDto[];

}

我也在使用内置的 nestjs 验证管道,这是我的引导程序:

async function bootstrap() {
  const app = await NestFactory.create(ServerModule);
  app.useGlobalPipes(new ValidationPipe());
  await app.listen(config.PORT);
}
bootstrap();

它对其他属性工作正常,对象数组是唯一不工作的。

【问题讨论】:

  • 我刚刚将您的代码放在一个空的示例项目中,它似乎对我有用。 “不工作”的具体价值是什么?你的期望是什么?例如,如果您将 "positions": [{"other": true}] 放入您的正文中,它会拒绝 400。positions: [] 是一个有效值。
  • 我希望如果你尝试positions: [1],它会抛出一个错误
  • @ArrayNotEmpty()?

标签: javascript node.js typescript nestjs class-validator


【解决方案1】:

您期望 positions: [1] 抛出 400 但它被接受了。

根据这个Github issue,这似乎是类验证器中的一个错误。如果您传入原始类型(booleanstringnumber、...)或 array 而不是对象,它将接受输入为有效,尽管它不应该。


除了创建 custom validation decorator 之外,我没有看到任何标准的解决方法:

import { registerDecorator, ValidationOptions, ValidationArguments } from 'class-validator';

export function IsNonPrimitiveArray(validationOptions?: ValidationOptions) {
  return (object: any, propertyName: string) => {
    registerDecorator({
      name: 'IsNonPrimitiveArray',
      target: object.constructor,
      propertyName,
      constraints: [],
      options: validationOptions,
      validator: {
        validate(value: any, args: ValidationArguments) {
          return Array.isArray(value) && value.reduce((a, b) => a && typeof b === 'object' && !Array.isArray(b), true);
        },
      },
    });
  };
}

然后在你的 dto 类中使用它:

@ValidateNested({ each: true })
@IsNonPrimitiveArray()
@Type(() => PositionDto)
positions: PositionDto[];

【讨论】:

    【解决方案2】:

    对我来说,我可以使用 'class-transformer' 验证嵌套对象

    import { Type } from 'class-transformer';
    

    完整示例:

    import {
      MinLength,
      MaxLength,
      IsNotEmpty,
      ValidateNested,
      IsDefined,
      IsNotEmptyObject,
      IsObject,
      IsString,
    } from 'class-validator';
    import { Type } from 'class-transformer';
    
    class MultiLanguageDTO {
      @IsString()
      @IsNotEmpty()
      @MinLength(4)
      @MaxLength(40)
      en: string;
    
      @IsString()
      @IsNotEmpty()
      @MinLength(4)
      @MaxLength(40)
      ar: string;
    }
    
    export class VideoDTO {
      @IsDefined()
      @IsNotEmptyObject()
      @IsObject()
      @ValidateNested()
      @Type(() => MultiLanguageDTO)
      name!: MultiLanguageDTO;
    }
    

    【讨论】:

      【解决方案3】:

      我遇到了同样的问题,所以我创建了自己的 ValidateNested 装饰器。

      import {
        ValidationOptions,
        registerDecorator,
        ValidationArguments,
        validateSync,
      } from 'class-validator';
      import { plainToClass } from 'class-transformer';
      
      /**
       * @decorator
       * @description A custom decorator to validate a validation-schema within a validation schema upload N levels
       * @param schema The validation Class
       */
      export function ValidateNested(
        schema: new () => any,
        validationOptions?: ValidationOptions
      ) {
        return function (object: Object, propertyName: string) {
          registerDecorator({
            name: 'ValidateNested',
            target: object.constructor,
            propertyName: propertyName,
            constraints: [],
            options: validationOptions,
            validator: {
              validate(value: any, args: ValidationArguments) {
                args.value;
                if (Array.isArray(value)) {
                  for (let i = 0; i < (<Array<any>>value).length; i++) {
                    if (validateSync(plainToClass(schema, value[i])).length) {
                      return false;
                    }
                  }
                  return true;
                } else
                  return validateSync(plainToClass(schema, value)).length
                    ? false
                    : true;
              },
              defaultMessage(args) {
                if (Array.isArray(args.value)) {
                  for (let i = 0; i < (<Array<any>>args.value).length; i++) {
                    return (
                      `${args.property}::index${i} -> ` +
                      validateSync(plainToClass(schema, args.value[i]))
                        .map((e) => e.constraints)
                        .reduce((acc, next) => acc.concat(Object.values(next)), [])
                    ).toString();
                  }
                } else
                  return (
                    `${args.property}: ` +
                    validateSync(plainToClass(schema, args.value))
                      .map((e) => e.constraints)
                      .reduce((acc, next) => acc.concat(Object.values(next)), [])
                  ).toString();
              },
            },
          });
        };
      }
      

      然后你可以像使用它一样 -

      class Schema2 {
      
        @IsNotEmpty()
        @IsString()
        prop1: string;
      
        @IsNotEmpty()
        @IsString()
        prop2: string;
      }
      
      
      class Schema1 {
        @IsNotEmpty()
        @IsString()
        prop3: string;
      
        @ValidateNested(Schema2)
        nested_prop: Schema2;
      }
      

      适用于非原始数组和 javascript 对象。

      【讨论】:

        猜你喜欢
        • 2021-04-22
        • 2021-03-09
        • 2012-09-22
        • 2021-05-05
        • 2019-05-08
        • 2022-08-02
        • 2020-06-05
        • 2020-12-02
        相关资源
        最近更新 更多