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

Custom Validators — Tworzenie własnych strażników

Wbudowane dekoratory class-validator pokrywają większość przypadków. Ale co jeśli potrzebujesz specjalistycznego strażnika, który sprawdza unikalne reguły twojego imperium? Czas nauczyć się tworzyć własnych strażników bramy!

@ValidatorConstraint — pełny custom validator

Najbardziej elastyczny sposób to stworzenie własnej klasy walidatora:

1import {
2  ValidatorConstraint,
3  ValidatorConstraintInterface,
4  ValidationArguments,
5} from 'class-validator';
6
7@ValidatorConstraint({ name: 'isRomanName', async: false })
8export class IsRomanNameConstraint implements ValidatorConstraintInterface {
9  validate(value: string, args: ValidationArguments): boolean {
10    // Sprawdź czy imię zaczyna się od wielkiej litery
11    // i zawiera tylko litery łacińskie
12    return /^[A-Z][a-z]+$/.test(value);
13  }
14
15  defaultMessage(args: ValidationArguments): string {
16    return 'Imię ($value) nie jest poprawnym imieniem rzymskim!';
17  }
18}

Klasa musi implementować interfejs

ValidatorConstraintInterface
z dwiema metodami:

  • validate()
    — zwraca
    true
    jeśli wartość jest poprawna
  • defaultMessage()
    — zwraca komunikat błędu

Użycie z @Validate()

1import { Validate } from 'class-validator';
2
3export class CreateLegionaryDto {
4  @Validate(IsRomanNameConstraint)
5  name: string;
6}

Tworzenie dekoratora — czystszy sposób

Zamiast używać

@Validate()
, możesz stworzyć własny dekorator:

1import {
2  registerDecorator,
3  ValidationOptions,
4  ValidatorConstraint,
5  ValidatorConstraintInterface,
6} from 'class-validator';
7
8@ValidatorConstraint({ async: false })
9class IsLegionCodeConstraint implements ValidatorConstraintInterface {
10  validate(value: string): boolean {
11    return /^LEG-[IVXLCDM]+-\d{3}$/.test(value);
12  }
13
14  defaultMessage(): string {
15    return 'Kod legionu musi mieć format LEG-[cyfry rzymskie]-[3 cyfry]';
16  }
17}
18
19export function IsLegionCode(validationOptions?: ValidationOptions) {
20  return function (object: object, propertyName: string) {
21    registerDecorator({
22      target: object.constructor,
23      propertyName: propertyName,
24      options: validationOptions,
25      constraints: [],
26      validator: IsLegionCodeConstraint,
27    });
28  };
29}

Teraz możesz używać go jak każdego innego dekoratora:

1export class CreateLegionaryDto {
2  @IsLegionCode({ message: 'Niepoprawny kod legionu!' })
3  legionCode: string;
4}

Walidatory asynchroniczne

Czasem walidacja wymaga dostępu do bazy danych (np. sprawdzenie czy nazwa jest unikalna). Wtedy tworzysz asynchroniczny walidator:

1@ValidatorConstraint({ name: 'isUniqueRank', async: true })
2export class IsUniqueRankConstraint implements ValidatorConstraintInterface {
3  constructor(private readonly legionService: LegionService) {}
4
5  async validate(rank: string): Promise<boolean> {
6    // Sprawdź w bazie czy ranga jest dostępna
7    const existing = await this.legionService.findByRank(rank);
8    return !existing;
9  }
10
11  defaultMessage(): string {
12    return 'Ta ranga jest już zajęta w legionie!';
13  }
14}

Uwaga: Asynchroniczne walidatory wymagają integracji z systemem Dependency Injection NestJS poprzez

useContainer
.

Walidacja krzyżowa pól

Możesz tworzyć walidatory, które sprawdzają relacje między polami:

1@ValidatorConstraint({ async: false })
2class IsAgeValidForRankConstraint implements ValidatorConstraintInterface {
3  validate(value: any, args: ValidationArguments): boolean {
4    const object = args.object as CreateLegionaryDto;
5    // Legatus musi mieć co najmniej 30 lat
6    if (object.rank === 'legatus' && object.age < 30) {
7      return false;
8    }
9    // Centurio musi mieć co najmniej 25 lat
10    if (object.rank === 'centurio' && object.age < 25) {
11      return false;
12    }
13    return true;
14  }
15
16  defaultMessage(args: ValidationArguments): string {
17    return 'Wiek nie odpowiada wymaganej randze!';
18  }
19}

Dzięki custom validatorom masz pełną kontrolę nad logiką walidacji. Twoi strażnicy bramy mogą teraz sprawdzać dowolne, nawet najbardziej złożone reguły!

Przejdź do CodeWorlds