使用 class-validator 验证每个 Map<string, number> 值

Mao*_*ion 7 validation class-validator nestjs

我正在尝试对由我的 DTO 之一建模的 JSON 输入执行简单的验证。对象属性之一的类型是Map<string, number>。输入示例:

{
  "type": "CUSTOM",
  "is_active": true,
  "current_plan_day": 1,
  "custom_warmup_plan": {
    "1": 123,
    "2": 456
}
Run Code Online (Sandbox Code Playgroud)

在我的控制器上,我使用 DTO 来指定主体类型。该类与类验证器装饰器一起是这样的:

    export class CreateWarmupPlanRequestDto {
      @IsEnum(WarmupPlanType)
      type: string;
    
      @IsOptional()
      @IsNumber({ allowInfinity: false, allowNaN: false, maxDecimalPlaces: 0 })
      @IsPositive()
      hard_cap: number | null;
    
      @IsBoolean()
      is_active: boolean;
    
      @IsNumber({ allowInfinity: false, allowNaN: false, maxDecimalPlaces: 0 })
      @IsPositive()
      current_plan_day: number;
    
      @IsOptional()
      @IsNumber({ allowInfinity: false, allowNaN: false, maxDecimalPlaces: 0 })
      @IsPositive()
      previous_plan_day: number | null;
    
      @IsOptional()
      @IsNumber({ allowInfinity: false, allowNaN: false, maxDecimalPlaces: 0 }, { each: true })
      @IsPositive({ each: true })
      custom_warmup_plan: Map<string, number>;  // PROBLEM HERE
    }
Run Code Online (Sandbox Code Playgroud)

我希望验证 的每个值custom_warmup_plan是否为现有的正整数。对象其他属性的验证工作正常且符合预期,但对于我的示例输入,我不断收到错误(2 个错误消息,已加入):

{
    "message": "each value in custom_warmup_plan must be a positive number. |#| each value in custom_warmup_plan must be a number conforming to the specified constraints",
    "statusCode": 400,
    "timestamp": "2021-07-29T13:18:29.331Z",
    "path": "/api/warmup-plan/bc4c3f0e-8e77-46de-a46a-a908edbdded5"
}
Run Code Online (Sandbox Code Playgroud)

这方面的文档似乎非常简单,但我就是无法让它发挥作用。我也尝试过一个简单的Map<string, string>验证@IsString(each: true)器,但这似乎也不起作用。

有任何想法吗?

版本:

"@nestjs/common": "^8.0.0",
"@nestjs/core": "^8.0.0",
"@nestjs/mapped-types": "^1.0.0",
"@nestjs/platform-express": "^8.0.0",
"class-transformer": "^0.4.0",
"class-validator": "^0.13.1",
Run Code Online (Sandbox Code Playgroud)

Dan*_*iel 1

来自文档

如果您的字段是一个数组,并且您想要对数组中的每个项目执行验证,则必须指定一个特殊的 every: true 装饰器选项

如果您希望能够验证映射,您可以编写一个自定义装饰器并传入class-validator函数列表来验证键和值。例如,下面的装饰器将键和值的验证函数列表作为输入(例如传入isStringisObject等等...,class-validator有一个相应的函数,您可以为它们提供的所有验证装饰器调用

export function IsMap(
  key_validators: ((value: unknown) => boolean)[],
  value_validators: ((value: unknown) => boolean)[],
  validationOptions?: ValidationOptions
) {
  return function (object: unknown, propertyName: string) {
    registerDecorator({
      name: 'isMap',
      target: (object as any).constructor,
      propertyName: propertyName,
      options: validationOptions,
      validator: {
        validate(value: unknown, args: ValidationArguments) {
          if (!isObject(value)) return false;
          const keys = Object.keys(value);
          const is_invalid = keys.some((key) => {
            const is_key_invalid = key_validators.some((validator) => !validator(key));
            if (is_key_invalid) return true;

            const is_value_invalid = value_validators.some((validator) => !validator(value[key]));
            return is_value_invalid;
          });

          return !is_invalid;
        },
      },
    });
  };
}
Run Code Online (Sandbox Code Playgroud)

你可以在你的例子中使用这个装饰器,如下所示

import { isInt } from 'class-validator'
export class CreateWarmupPlanRequestDto {
  @IsOptional()
  @IsMap([], [isInt])
  custom_warmup_plan: Map<string, number>;
}
Run Code Online (Sandbox Code Playgroud)