Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Walidacja zagnieżdżona — Inspekcja głęboka

Dotychczas walidowaliśmy proste, płaskie obiekty. Ale co jeśli przychodzące dane mają zagnieżdżone obiekty lub tablice obiektów? Potrzebujemy głębokiej inspekcji — strażnicy muszą sprawdzić nie tylko osobę na bramie, ale też jej bagaż, dokumenty w torbie i przedmioty w kieszeniach.

Problem — płaska walidacja nie wystarczy

Wyobraź sobie DTO dla legionu, który zawiera listę legionistów:

1// BEZ zagnieżdżonej walidacji
2export class CreateLegioDto {
3  @IsString()
4  name: string;
5
6  @IsArray()
7  soldiers: CreateSoldierDto[];  // Tablica obiektów
8  // Ale czy każdy obiekt w tablicy jest poprawny? NIE WIEMY!
9}

Samo

@IsArray()
sprawdzi tylko czy wartość jest tablicą — nie zwaliduje zawartości obiektów wewnątrz!

@ValidateNested() — strażnik zagnieżdżonych obiektów

Dekorator

@ValidateNested()
mówi class-validator: "Sprawdź też walidację obiektu wewnętrznego":

1import { ValidateNested } from 'class-validator';
2import { Type } from 'class-transformer';
3
4class CreateSoldierDto {
5  @IsString()
6  @IsNotEmpty()
7  name: string;
8
9  @IsNumber()
10  @Min(16)
11  age: number;
12}
13
14export class CreateLegioDto {
15  @IsString()
16  @IsNotEmpty()
17  name: string;
18
19  @ValidateNested()
20  @Type(() => CreateSoldierDto)
21  commander: CreateSoldierDto;
22}

Ważne:

@ValidateNested()
zawsze wymaga
@Type()
z class-transformer! Bez niego NestJS nie wie, na jaką klasę skonwertować surowy obiekt.

Walidacja tablic zagnieżdżonych obiektów

Gdy masz tablicę obiektów, musisz dodać

{ each: true }
:

1export class CreateLegioDto {
2  @IsString()
3  name: string;
4
5  @ValidateNested({ each: true })
6  @Type(() => CreateSoldierDto)
7  soldiers: CreateSoldierDto[];
8}

Parametr

{ each: true }
mówi dekoratorowi, żeby sprawdził każdy element tablicy osobno. Bez niego walidacja zagnieżdżona nie zadziała na tablicach.

Wielopoziomowe zagnieżdżenie

Zagnieżdżenie może sięgać wielu poziomów głębokości:

1class WeaponDto {
2  @IsString()
3  @IsNotEmpty()
4  name: string;
5
6  @IsNumber()
7  @Min(1)
8  damage: number;
9}
10
11class SoldierDto {
12  @IsString()
13  @IsNotEmpty()
14  name: string;
15
16  @ValidateNested()
17  @Type(() => WeaponDto)
18  primaryWeapon: WeaponDto;
19
20  @IsOptional()
21  @ValidateNested()
22  @Type(() => WeaponDto)
23  secondaryWeapon?: WeaponDto;
24}
25
26class CohortDto {
27  @IsString()
28  name: string;
29
30  @ValidateNested({ each: true })
31  @Type(() => SoldierDto)
32  soldiers: SoldierDto[];
33}
34
35export class CreateLegioDto {
36  @IsString()
37  name: string;
38
39  @ValidateNested({ each: true })
40  @Type(() => CohortDto)
41  cohorts: CohortDto[];
42}

W tym przykładzie NestJS zwaliduje:

  • Czy
    CreateLegioDto.cohorts
    to tablica poprawnych
    CohortDto
  • Czy każdy
    CohortDto.soldiers
    to tablica poprawnych
    SoldierDto
  • Czy każdy
    SoldierDto.primaryWeapon
    to poprawny
    WeaponDto

Komunikaty błędów zagnieżdżonych

Gdy walidacja zagnieżdżona nie przejdzie, NestJS zwróci szczegółowy błąd ze ścieżką do problematycznego pola:

1// Przykładowa odpowiedź błędu:
2{
3  "statusCode": 400,
4  "message": [
5    "cohorts.0.soldiers.1.name should not be empty",
6    "cohorts.0.soldiers.1.primaryWeapon.damage must be at least 1"
7  ],
8  "error": "Bad Request"
9}

Głęboka inspekcja to kluczowa umiejętność strażników bramy. Bez niej złośliwy użytkownik mógłby przemycić niepoprawne dane w zagnieżdżonych obiektach!

Przejdź do CodeWorlds