node.js - Nestjs class-validator undantag - hur man kastar metadata information för @IsNotIn validator


Jag har en NestJs dto som ser ut så här

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;
}

Jag använder ett undantagsfilter som returnerar fel med tydliga detaljer om vilken validering som misslyckades och för vilket fält.

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',
    });
  },
}),

);

vilket returnerar ett fel som ser ut ungefär så här

{"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"}

Men för den misslyckade valideringen som @NotIn skulle jag vilja att felet var mer specifikt när det gäller vilka som är reserverade nyckelord, och vill att de returneras i felet som en separat nyckel som:

{"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"}

Men detta block från undantagsfiltret returnerar inte begränsningsvärdena med dekoratormetadatan.

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

Svar #1

Enligt min kunskap finns det inget koncept för att automatiskt returnera metadata eller liknande. Eftersom du lagrar de svartlistade värdena i variablerna kan du tillhandahålla det som context, vilket är mappat till särskilda feltyper.

const restrictedNames = ['Name Inc', 'Acme Inc'] as const;
class DTO {
  @IsNotEmpty()
  name: string;
  @IsEmail()
  email: string;

  @IsNotEmpty()
  @IsNotIn(restrictedNames, { context: { reservedKeywords: restrictedNames } }) // here
  orgName: string;

  // You may ignore the contructor
  constructor(name: string, email: string, orgName: string) {
    this.name = name;
    this.email = email;
    this.orgName = orgName;
  }
}

Nu behöver du bara inkludera värdet villkorligt i din transformfunktion med error.contexts

const transformedMessage = errors.reduce((acc, error) => {
  acc[error.property] = Object.keys(error.constraints).map((failed) => ({
    failedValidation: failed,
    message: error.constraints[failed],
    data: error.contexts && error.contexts[failed],
  }));
  return acc;
}, {});

Som du vill, här är resultatet.

{
  "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"
        ]
      }
    }
  ]
}

PS: Tack för att du tillhandahöll transformfunktionen. Jag stjäl den skamlöst.

Relaterade artiklar