node.js - NestJs - Valider forespørselskropp ved å bruke class-validator med 2 alternativer for kroppsklasse


Jeg har et REST-kall, som kan motta en kropp av typen classA eller classB. Jeg trenger å beholde det som 2 forskjellige klasser. Eksempel -

// classes -
class ClassA {
    @IsString()
    @Length(1, 128)
    public readonly name: string;
    @IsString()
    @Length(1, 128)
    public readonly address: string;
}

class ClassB {
    @IsString()
    @Length(1, 10)
    public readonly id: string;
}


// my request controller - 

    @Post('/somecall')
    public async doSomething(
        @Body(new ValidationPipe({transform: true})) bodyDto: (ClassA | ClassB) // < not validating any of them..
    ): Promise<any> {
// do something
    }

Problemet er at når man har mer enn én klasse, blir ikke kroppen validert.

Hvordan kan jeg bruke 2 eller flere klasser og validere dem ved hjelp av class-validator? Jeg vil ikke bruke samme klasse..

Takk til dere alle :)

Svar #1

Jeg vil ikke bruke samme klasse..

Da vil det ikke være mulig, i det minste ikke med Nests innebygde ValidationPipe. Typescript reflekterer ikke unions-, snitt- eller andre typer generiske typer, så det finnes ingen returnert metadata for denne parameteren, og hvis det ikke finnes metadata som kan brukes, vil Nest ende opp med å hoppe over pipen.

Du kan sannsynligvis lage en tilpasset pipe for å gjøre valideringen for deg, og hvis du har to typer kommer du sannsynligvis til å måtte gjøre det. Du kan fortsatt kalle de aktuelle class-transformer og class-validator metodene inne i klassen også.

Svar #2

Støtte på en lignende situasjon der jeg måtte validere en forespørsel med unionstype.
Løsningen jeg endte opp med var en tilpasset pipe som Jay McDoniel foreslo her. Logikken vil variere avhengig av hvilken forespørselskropp du håndterer, men i henhold til spørsmålet kan følgende kanskje fungere

Tilpasset rør:

import { ArgumentMetadata, BadRequestException, Inject, Scope } from "@nestjs/common";
import { PipeTransform } from "@nestjs/common";
import { plainToInstance } from "class-transformer";
import { validate } from "class-validator";
import { ClassADto } from '../repository/data-objects/class-a.dto';
import { ClassBDto } from '../repository/data-objects/class-b.dto';

export class CustomPipeName implements PipeTransform<any> {

    async transform(value: any,  { metatype, type }: ArgumentMetadata): Promise<any> {

        if (type === 'body') {
            const classA = plainToInstance(ClassADto, value);
            const classB = plainToInstance(ClassBDto, value);
            const classAValidationErrors = await validate(classA);
            const classBValidationErrors = await validate(classB);
            if (classAValidationErrors.length > 0 && classBValidationErrors.length > 0) {
                throw new BadRequestException('some fancy info text');
            }
        }
        return value;
    }
}

Kontrollerbruk:

@Post('/somecall')
    public async doSomething(
        @Body(new CustomePipeName()) bodyDto: (ClassA | ClassB)
    ): Promise<any> {
// do something
    }

Relaterte artikler