Dekoratory z poprzednich lekcji -
@IsString, @IsNumber, @Min - niczego same nie sprawdzają. To opis wymagań przypięty do pól, tak jak wykaz przewozowy przypięty do skrzyni. Ktoś musi ten wykaz wziąć do ręki i porównać z zawartością.Główną rolą
jest automatyczne uruchamianie walidacji dekoratorów ValidationPipe
. Nie szyfruje danych w żądaniach HTTP, nie kompresuje odpowiedzi serwera i nie generuje dokumentacji Swagger. Robi jedno: bierze przychodzący obiekt, odczytuje dekoratory z klasy DTO i sprawdza, czy dane im odpowiadają.class-validator
Pipe rejestruje się raz, przy starcie aplikacji:
1async function bootstrap() {
2 const app = await NestFactory.create(AppModule);
3
4 app.useGlobalPipes(
5 new ValidationPipe({
6 whitelist: true,
7 forbidNonWhitelisted: true,
8 transform: true,
9 }),
10 );
11
12 await app.listen(3000);
13}Zapis czyta się w czterech częściach:
to obiekt aplikacji, app
otwiera rejestrację, .useGlobalPipes(
tworzy pipe z opcjami, a new ValidationPipe({ ... })
domyka wywołanie.)
Zwróć uwagę na liczbę mnogą w nazwie metody:
useGlobalPipes przyjmuje wiele pipe'ów po przecinku. W praktyce prawie zawsze podajesz jeden - ValidationPipe - bo pozostałe, jak ParseIntPipe, mają sens tylko przy konkretnych parametrach.Konfiguracja pipe'a to nie zbiór niezależnych przełączników, tylko drabina restrykcyjności. Warto ją znać w całości, bo wybór trybu jest decyzją o tym, jak twoje API traktuje nieznane dane.
ValidationPipe - brak walidacji. Do handlera trafia dowolny obiekt JSON. Adnotacje typów w kodzie są wtedy życzeniem, nie gwarancją.ValidationPipe() - podstawowa walidacja. Reguły z dekoratorów działają, ale pola spoza DTO przechodzą nietknięte.whitelist: true - usuwanie nieznanych pól.whitelist + forbidNonWhitelisted - odrzucanie żądań.Dwa ostatnie tryby dzieli jedna rzecz i warto ją zapamiętać dokładnie. Opcja
automatycznie usuwa z przychodzących danych pola bez dekoratorów walidacji. Nie dodaje białego tła do odpowiedzi, nie tworzy listy dozwolonych adresów IP i nie wyłącza walidacji na wybranych endpointach - po prostu wycina to, czego nie opisałeś w DTO.whitelist: true
Różnica wobec
jest taka, że forbidNonWhitelisted: true
usuwa nieznane pola po cichu, a whitelist
zwraca błąd forbidNonWhitelisted
. Nie są to opcje identyczne, 400
forbidNonWhitelisted niczego nie wyłącza i nie dopisuje pól do logów serwera.1// DTO opisuje tylko: name, cohortId
2// Klient przysyła: { name: 'Marcus', cohortId: 3, isAdmin: true }
3
4// ValidationPipe() -> serwis dostaje isAdmin: true
5// whitelist: true -> serwis dostaje { name, cohortId }
6// whitelist + forbidNonWhitelisted -> 400 Bad RequestPole
isAdmin w tym przykładzie nie jest przypadkowe. Bez whitelist trafia do serwisu, a jeśli gdzieś dalej ktoś zrobi Object.assign(user, dto), klient właśnie nadał sobie uprawnienia. To najstarszy sposób na przejęcie konta w aplikacjach, które ufają wejściu.Który tryb wybrać?
forbidNonWhitelisted jest surowszy, a przez to uczciwszy: klient dowiaduje się, że przysłał coś nieoczekiwanego, zamiast myśleć, że zapisałeś jego pole. Samo whitelist bywa wygodniejsze przy publicznym API, w którym starsze wersje klientów przysyłają pola już wycofane.Ostatnia opcja z zestawu produkcyjnego dotyczy typów.
sprawia, że do handlera trafia instancja klasy DTO, a nie zwykły obiekt - i przy okazji konwertuje typy proste. Bez niej transform: true
@Param('id') id: number daje napis '7', choć typ mówi number, bo z adresu URL wszystko przychodzi jako tekst.To także warunek działania dekoratorów
@Type i @Transform z class-transformer: bez transform: true pozostają one adnotacjami bez skutku.Gdy jeden endpoint potrzebuje innych reguł niż reszta, pipe podpina się lokalnie przez
@UsePipes(new ValidationPipe({ ... })) nad metodą. Pipe lokalny ma pierwszeństwo przed globalnym.Sięgaj po to rzadko. Konfiguracja walidacji rozproszona po kontrolerach szybko przestaje być czytelna, a odpowiedź na pytanie „czy ten endpoint odrzuca nadmiarowe pola?" zaczyna wymagać czytania trzech plików.
Wykaz przewozowy sam niczego nie sprawdzi, @name:
ValidationPipe jest automatyczne uruchamianie walidacji dekoratorów class-validator - nie szyfrowanie, nie kompresja, nie generowanie Swaggera,app → .useGlobalPipes( → new ValidationPipe({ ... }) → ),ValidationPipe → ValidationPipe() → whitelist: true → whitelist + forbidNonWhitelisted,whitelist: true usuwa z przychodzących danych pola bez dekoratorów walidacji - nie dotyczy adresów IP ani wyłączania walidacji,whitelist usuwa nieznane pola po cichu, forbidNonWhitelisted zwraca błąd 400,isAdmin to najprostsza droga do nadania sobie uprawnień,transform: true daje instancję DTO i konwertuje typy proste; bez niej @Type i @Transform nie działają,@UsePipes nad metodą nadpisuje konfigurację globalną - używaj oszczędnie.W następnej lekcji zajmiemy się
class-transformer - tym, co dzieje się z danymi w drugą stronę, przy wychodzeniu z API. A na razie zapamiętaj: dekoratory opisują, pipe wykonuje. Bez niego masz w kodzie bardzo dokładną dokumentację reguł, których nikt nie egzekwuje.