首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用类验证器验证每个Map<string、number>值

使用类验证器验证每个Map<string、number>值
EN

Stack Overflow用户
提问于 2021-07-29 13:22:45
回答 3查看 1.7K关注 0票数 6

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

代码语言:javascript
复制
{
  "type": "CUSTOM",
  "is_active": true,
  "current_plan_day": 1,
  "custom_warmup_plan": {
    "1": 123,
    "2": 456
}

在我的控制器上,我使用DTO来指定body类型。类以及类验证器装饰器是这样的:

代码语言:javascript
复制
    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
    }

我希望验证custom_warmup_plan的每个值是一个现有的正整数。对对象的其他属性的验证工作得很好,就像预期的那样,但是对于我的示例输入,我总是收到错误(2条错误消息,已加入):

代码语言:javascript
复制
{
    "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"
}

这方面的文档看起来很直截了当,但我只是无法让它发挥作用。我还玩过一个简单的Map<string, string>@IsString(each: true)验证器,但这似乎也不起作用。

有什么想法吗?

版本:

代码语言:javascript
复制
"@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",
EN

回答 3

Stack Overflow用户

发布于 2022-06-09 04:32:32

将普通对象转换为映射是必要的。使用类变压器的变换装饰器

代码语言:javascript
复制
@IsOptional()
@IsNumber(undefined, { each: true })
@Transform(({ value }) => new Map(Object.entries(value)))
prop?: Map<string, number>;
票数 2
EN

Stack Overflow用户

发布于 2022-04-09 15:25:12

来自文档

如果您的字段是数组,并且要对数组中的每一项执行验证,则必须指定特殊的“每一项:真正的装饰器”选项。

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

代码语言:javascript
复制
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 false;

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

          return !is_invalid;
        },
      },
    });
  };
}

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

代码语言:javascript
复制
import { isInt } from 'class-validator'
export class CreateWarmupPlanRequestDto {
  @IsOptional()
  @IsMap([], [isInt])
  custom_warmup_plan: Map<string, number>;
}
票数 0
EN

Stack Overflow用户

发布于 2022-11-19 08:39:13

使用与@Daniel相同的方法,我稍微修改了代码,以便将焦点放在“isValid”上,而不是“IsInvalid”。这样我们才能避免双重否定。另外,将即将出现的对象转换为DTO中的映射。

代码语言:javascript
复制
@Transform(({ value }) => new Map(Object.entries(value)))
代码语言:javascript
复制
import {
    registerDecorator,
    ValidationArguments,
    ValidationOptions,
} from 'class-validator';
import * as $$ from 'lodash';

export function IsMap(
    keyValidators: ((value: unknown) => boolean)[],
    valueValidators: ((value: unknown) => boolean)[],
    validationOptions?: ValidationOptions,
) {
    return function (object: unknown, propertyName: string) {
        /**
         * ** value is expected to be a MAP already, we are just checking types of keys and values...
         */
        registerDecorator({
            name: 'isMap',
            target: (object as any).constructor,
            propertyName: propertyName,
            options: validationOptions,
            validator: {
                validate(value: Map<any, any>, args: ValidationArguments) {
                    if (!$$.isMap(value)) {
                        return false;
                    }
                    const keys = Array.from(value.keys());
                    return $$.every(keys, (key) => {
                        // checking if keys are valid...
                        const isKeyInvalid = keyValidators.some(
                            (validator) => !validator(key),
                        );
                        if (isKeyInvalid) {
                            return false;
                        }
                        // checking if values are valid...
                        const isValueInvalid = valueValidators.some(
                            (validator) => !validator(value.get(key)),
                        );
                        if (isValueInvalid) {
                            return false;
                        } else {
                            return true;
                        }
                    });
                },
            },
        });
    };
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68576734

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档