class-validator 带有自定义错误消息的关系验证

class-validator relational validation with custom error messages

所以我正在构建一个 API,用户在请求正文中给我们 typevaluetype 可能是 CNIC, EMAIL, MOBILE

现在基于 type 我必须验证值,例如 EMAIL 是否有效或 MOBILE 是否有效等

因此我们可以看到 value 字段依赖于 type 字段来验证它。

我需要一种使用 class-validator validationPipe.

来处理这个问题的方法

所以起初我快速打开文档并开始查找高级用法,我开始知道自定义装饰器肯定会帮助我并且它确实使用了下面的示例。

validate-value-by-type.decorator.ts

import {
  registerDecorator,
  ValidationOptions,
  ValidationArguments,
  isEmail,
} from 'class-validator';
import { CA_DetailsTypes } from './models';

export function ValidateByAliasType(
  property: string,
  validationOptions?: ValidationOptions,
) {
  // eslint-disable-next-line @typescript-eslint/ban-types
  return function (object: Object, propertyName: string) {
    registerDecorator({
      name: 'validateByAliasType',
      target: object.constructor,
      propertyName: propertyName,
      constraints: [property],
      options: validationOptions,
      validator: {
        validate(value: any, args: ValidationArguments) {
          const [relatedPropertyName] = args.constraints;
          const relatedValue = (args.object as any)[relatedPropertyName];
          if (relatedValue === CA_DetailsTypes.EMAIL) {
            return isEmail(value) && value.length > 5 && value.length <= 99;
          }
          if (relatedValue === CA_DetailsTypes.CNIC) {
            return value.length === 13;
          }
          if (relatedValue === CA_DetailsTypes.MOBILE) {
            return value.length === 11;
          }
          if (relatedValue === CA_DetailsTypes.TXT) {
            return value.length > 3 && value.length <= 99;
          }
          return false;
        },
      },
    });
  };
}

client.request.dto.ts

import { ApiProperty } from '@nestjs/swagger';
import { IsEnum, IsNotEmpty } from 'class-validator';

export enum CA_DetailsTypes {
  'CNIC' = 'CNIC',
  'MOBILE' = 'MOBILE',
  'EMAIL' = 'EMAIL',
  'TXT' = 'TXT',
}

export class CA_FetchDetails_DTO {
  @ApiProperty({
    example: 'MOBILE',
    type: 'enum',
    enum: CA_DetailsTypes,
  })
  @IsNotEmpty()
  @IsEnum(CA_DetailsTypes)
  type: CA_DetailsTypes;

  @ApiProperty({ example: '03070000002' })
  @ValidateByAliasType('type')
  value: string;
}

因此,对于上面的示例,我可以通过 type 成功验证 value,但我仍然无法根据 type 自定义错误消息。

所以经过一天的研究,我失去了所有的希望,决定使用另一个库来进行这个复杂的验证,我这样做了,但是那个库有一个缺点,我最终回到了 class-validator 经过大量研究,我找到了一种基于 type.

自定义错误消息的方法

所以这里是基于type自定义错误消息的代码,我使用defaultMessage()来自定义它。

validate-value-by-type.decorator.ts

import {
  registerDecorator,
  ValidationOptions,
  ValidationArguments,
  isEmail,
} from 'class-validator';
import { CA_DetailsTypes } from './models';

export function ValidateByAliasType(
  property: string,
  validationOptions?: ValidationOptions,
) {
  // eslint-disable-next-line @typescript-eslint/ban-types
  return function (object: Object, propertyName: string) {
    registerDecorator({
      name: 'validateByAliasType',
      target: object.constructor,
      propertyName: propertyName,
      constraints: [property],
      options: validationOptions,
      validator: {
        validate(value: any, args: ValidationArguments) {
          const [relatedPropertyName] = args.constraints;
          const relatedValue = (args.object as any)[relatedPropertyName];
          if (relatedValue === CA_DetailsTypes.EMAIL) {
            return isEmail(value) && value.length > 5 && value.length <= 99;
          }
          if (relatedValue === CA_DetailsTypes.CNIC) {
            return value.length === 13;
          }
          if (relatedValue === CA_DetailsTypes.MOBILE) {
            return value.length === 11;
          }
          if (relatedValue === CA_DetailsTypes.TXT) {
            return value.length > 3 && value.length <= 99;
          }
          return false;
        },
        defaultMessage(args?: ValidationArguments) {
          const [relatedPropertyName] = args.constraints;
          const relatedValue = (args.object as any)[relatedPropertyName];
          switch (relatedValue) {
            case CA_DetailsTypes.EMAIL:
              return 'Please enter valid email!';

            case CA_DetailsTypes.MOBILE:
              return 'Please enter valid mobile!';

            case CA_DetailsTypes.CNIC:
              return 'Please enter valid CNIC!';

            default:
              return 'Invalid value!';
          }
        },
      },
    });
  };
}

终于完成了!