NodeJS Nestjs类验证器异常-如何为@IsNotIn验证器抛出元数据信息

oknwwptz  于 2023-06-05  发布在  Node.js
关注(0)|答案(1)|浏览(302)

我有一个NestJs dto,看起来像这样

import { IsEmail, IsNotEmpty, IsNotIn } from 'class-validator';
import { AppService } from './app.service';
const restrictedNames = ['Name Inc', 'Acme Inc'];
class DTO {
  @IsNotEmpty()
  name: string;
  @IsEmail()
  email: string;

  @IsNotEmpty()
  @IsNotIn(restrictedNames)
  orgName: string;
}

我使用的是一个异常过滤器,它返回的错误中包含关于哪个验证失败以及哪个字段失败的明确详细信息。

app.useGlobalPipes(
new ValidationPipe({
  exceptionFactory: (validationErrors: ValidationError[] = []) => {
    console.log(validationErrors);
    return new BadRequestException({
      statusCode: HttpStatus.BAD_REQUEST,
      message: validationErrors.reduce((acc, error) => {
        acc[error.property] = Object.keys(error.constraints).map(
          (failed) => ({
            failedValidation: failed,
            message: error.constraints[failed],
          }),
        );
        return acc;
      }, {}),
      error: 'validation',
    });
  },
}),

);
它会返回一个类似这样的错误

{"statusCode":400,"message":{"email":[{"failedValidation":"isEmail","message":"email must be an email"}],"orgName":[{"failedValidation":"isNotIn","message":"orgName should not be one of the following values: Name Inc, Acme Inc"}]},"error":"validation"}

但是对于失败的验证,例如@NotIn,我希望错误更具体地说明保留关键字是什么,并希望它们在错误中作为一个单独的键返回,如:

{"statusCode":400,"message":{"email":[{"failedValidation":"isEmail","message":"email must be an email"}],"orgName":[{"failedValidation":"isNotIn","message":"orgName should not be one of the following values: Name Inc, Acme Inc", "data":{"reservedKeywords":["Name Inc","Acme Inc"]}}]},"error":"validation"}

但是这个来自异常Filter的块没有返回带有装饰器元数据的约束值。

message: validationErrors.reduce((acc, error) => {
        acc[error.property] = Object.keys(error.constraints).map(
          (failed) => ({
            failedValidation: failed,
            message: error.constraints[failed],
          }),
        );
        return acc;
      }, {}),
      error: 'validation',
    });
iqjalb3h

iqjalb3h1#

你可以通过覆盖@IsNotIn装饰器来实现你想要的结果。您可以从class-validator创建一个使用registerDecorator的自定义装饰器,并在自定义装饰器中添加逻辑,以在错误消息中包含保留关键字数组。
你可以这样做:

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

export function IsNotInRestrictedNames(validationOptions?: ValidationOptions) {
    return (object: Object, propertyName: string) => {
        registerDecorator({
            name: "IsNotInRestrictedNames",
            target: object.constructor,
            propertyName: propertyName,
            constraints: [],
            options: validationOptions,
            validator: {
                validate(value: any, args: ValidationArguments) {
                    const [relatedPropertyName] = args.constraints;
                    const relatedValue = (args.object as any)[relatedPropertyName];
                    const restrictedNames = ['Name Inc', 'Acme Inc'];
                    if (restrictedNames.includes(value)) {
                        return false;
                    }
                    return true;
                },
                defaultMessage(args: ValidationArguments) {
                    const restrictedNames = ['Name Inc', 'Acme Inc'];
                    return `${args.property} should not be one of the following values: ${restrictedNames.join(", ")}, reservedKeywords: ${JSON.stringify(restrictedNames)}`;
                }
            }
        });
    };
}

然后,将DTO中的@IsNotIn(restrictedNames)替换为@IsNotInRestrictedNames()

class DTO {
  @IsNotEmpty()
  name: string;
  @IsEmail()
  email: string;

  @IsNotEmpty()
  @IsNotInRestrictedNames()
  orgName: string;
}

这将在错误消息中包含restrictedNames

相关问题