Validacja w Express.js - okładka

Validacja w Express.js z pomocą express-validator

Opublikowano Kategorie Backend, JavaScriptCzas czytania 7min

Przede wszystkim, czym jest validacja i do czego jej potrzebujemy?

Validacją nazywamy szereg czynności polegający na sprawdzeniu zgodności z danymi schematami i wytycznymi, a także naszymi oczekiwaniami i przewidywaniami. Validację stosuje się najczęściej do sprawdzania poprawności danych wejściowych. Moim zdaniem sprawdzanie poprawności otrzymywanych danych jest absolutną koniecznością, jeśli chcemy, by nasza aplikacja działała poprawnie.

Validacja - Klient serwer

Gdzie wykonywać validację?

Czynność tą powinniśmy wykonywać zarówno po stronie klienta, jak i serwera. Podczas wypełniania np. formularza użytkownik powinien otrzymać informację zwrotną, jakie pola uzupełnił błędnie oraz co należy w nich poprawić. Głównym celem jest zwiększenie komfortu użytkownika podczas korzystania z naszej aplikacji. Nie chroni nas to jednak przed umyślnym wprowadzeniem niepoprawnych danych. Mechanizmy znajdujące się po stronie klienta można w banalny sposób obejść. Dlatego też sprawdzanie poprawności danych powinno przede wszystkim odbywać się na serwerze.

Praktyczny przykład validacji na serwerze

Kluczowym zagadnieniem tego artykułu jest omówienie możliwości paczki express-validator będącej rozszerzeniem frameworka Express.js. Artykuł przygotowano bazując na wersji 5.3.0. W nowszych wersjach biblioteki mogą wystąpić różnice, więc aby być na bieżąco, zachęcam do sprawdzenia odicjalnej dokumentacji. Jak sama nazwa wskazuje, rozszerzenie to umożliwia nam tworzenie validatorów. W tym wpisie będziemy sprawdzać poprawność danych podczas rejestracji nowego użytkownika. Na samym początku musimy zainstalować paczkę express-validator.


npm install --save express-validator

Po zainstalowaniu pakietu możemy już importować interesujące nas komponenty. Przede wszystkim potrzebujemy skorzystać z funkcji check. Funkcja check jako parametr przyjmuje string z nazwą pola, które należy sprawdzić. Potem następuje spora dowolność w validacji. Tutaj możemy korzystać z istniejących w JS funkcji jak chociażby trim, którą możemy obciąć nadmiar białych znaków. Innym przykładem może być matches, gdzie możemy skorzystać z wyrażenia regularnego.

Oprócz tego możemy skorzystać z funkcji dostępnych dzięki naszej paczce. Jednymi z najbardziej przydatnych jest isEmpty, która sprawdza, czy nie otrzymaliśmy pustego ciągu znaków. Możemy też skorzystać z negacji not, łącząc ją z poprzednią funkcją. Otrzymujemy dzięki temu kombinację sprawdzającą, czy dany ciąg znaków nie jest pusty — niezmiernie przydatne w trakcie validacji. Podobnie do funkcji isEmpty działają funkcje takie jak: isArray, isEmail i isBoolean. Jeśli chcemy sprawdzać jakąś wartość, ale nie chcemy, aby jej podanie było obowiązkowe, możemy skorzystać z funkcji optional. Samych funkcji oczywiście jest znacznie więcej. Link do dokumentacji znajdziesz w źródłach na końcu tego wpisu.

Zwieńczeniem validatora jest funkcja withMessage zwracająca opracowany przez nas komunikat w przypadku, gdy nasze dane nie przejdą validacji. Należy pamiętać, aby komunikat zwrotny jednoznacznie określał, na czym polega błąd, który popełnił użytkownik. Dlatego też w swoich przykładach sprawdzam niektóre pola kilkukrotnie, ponieważ potrzebuję sprawdzić więcej niż jedną rzecz. Każdy przypadek powinien posiadać osobny, komunikat. Zwróć uwagę na pola takie jak password czy email.


import { check } from 'express-validator/check';

exports.validateRegister = [
    check('name').trim().not().isEmpty().withMessage('Name is required.'),
    check('password').trim().not().isEmpty().withMessage('Password is required.'),
    check('password').trim().isLength({ min: 8 }).withMessage('Password must be at least 8 characters long.'),
    check('email').trim().not().isEmpty().withMessage('E-mail is required.'),
    check('email').trim().isEmail().withMessage('Invalid e-mail address.'),
    check('pictures').optional().isArray().withMessage('Invalid data format.')
];

Sanityzacja

Kolejnym krokiem będzie sanityzacja danych. Sama sanityzacja pozwala nam uchronić się np. przed atakami takimi jak na przykład XSS. Zainstalowana przez nas paczka posiada również mechanizmy pozwalające na sanityzację danych i idąc tropem XSS, zamianę znaków specjalnych jak na przykład nawiasy domknięte czy slesze na encje. Do tej czynności posłuży nam funkcja sanitizeBody, która jako parametr przyjmuje string z nazwą pola. Alternatywnie możemy użyć gwiazdki, jeśli chcemy pracować z każdą przekazaną wartością. Do zamiany na encje posłuży nam funkcja escape.


import { check } from 'express-validator/check';
import { sanitizeBody } from 'express-validator/filter';

exports.validateRegister = [
    check('name').trim().not().isEmpty().withMessage('Name is required.'),
    check('password').trim().not().isEmpty().withMessage('Password is required.'),
    check('password').trim().isLength({ min: 8 }).withMessage('Password must be at least 8 characters long.'),
    check('email').trim().not().isEmpty().withMessage('E-mail is required.'),
    check('email').trim().isEmail().withMessage('Invalid e-mail address.'),
    check('pictures').optional().isArray().withMessage('Invalid data format.'),
    sanitizeBody('*').escape()
];

Customowe funkcje validujące

Omawiana w tym wpisie bibliotek udostępnia również tworzenie niestandardowych filtrów. Służy do tego funkcja custom. Wewnątrz tej funkcji możemy stworzyć własne funkcje validujące.

Validacja - czas

W moim projekcie wykorzystałem funkcję custom do sprawdzania poprawności wpisywanej daty. Niestety sama biblioteka express-validator nie udostępnia domyślnie takiej możliwości. Do sprawdzenia poprawności daty użyłem biblioteki Moment.js, która jest dedykowana operacjom na dacie i czasie.


import { check } from 'express-validator/check';
import { sanitizeBody } from 'express-validator/filter';
import moment from 'moment';

exports.validateRegister = [
    check('name').trim().not().isEmpty().withMessage('Name is required.'),
    check('password').trim().not().isEmpty().withMessage('Password is required.'),
    check('password').trim().isLength({ min: 8 }).withMessage('Password must be at least 8 characters long.'),
    check('email').trim().not().isEmpty().withMessage('E-mail is required.'),
    check('email').trim().isEmail().withMessage('Invalid e-mail address.'),
    check('birthDate').trim().not().isEmpty().withMessage('Date of birth is required.'),
    check('birthDate').trim().custom(value => moment(value, 'YYYY-MM-DD').isValid()).withMessage('Invalid date of birth.'),
    check('pictures').optional().isArray().withMessage('Invalid data format.'),
    sanitizeBody('*').escape()
];

Implementacja

Mając już stworzone reguły, należy ich użyć. Oprócz samych reguł potrzebujemy jeszcze funkcji, która powiadomi użytkownika, jakie dane wprowadził niepoprawnie i co zależy w nich poprawić. Poniżej znajdziesz gotowe rozwiązanie, które możesz wykorzystać w swoim projekcie.


import {validationResult} from "express-validator/check";

exports.checkValidation = (req, res, next) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
        return res.status(422).json({
            success: false,
            data: req.body,
            errors: errors.mapped()
        });
    }
    next();
};

Ostatnim krokiem będzie zaimplementowanie stworzonego przez nas kodu. W moim przypadku będzie to router, w którym znajduje się ścieżka do tworzenia nowego konta użytkownika.


import { Router } from "express";
import authController from "../controllers/authController";
import { validateRegister } from "../validators/usersValidator";
import { checkValidation } from "../validators/checkValidation";

const api = Router();

api.post('/register',
    validateRegister,
    checkValidation,
    authController.register
);

module.exports = api;

Więcej przykładów

W tym wpisie nie przedstawiłem wszystkich możliwości express-validatora. Myślę jednak, że zestaw informacji, jaki przedstawiłem, pozwoli na szybkie wsiąknięcie w temat validacji w Express.js.

Przedstawiony w tym wpisie kod jest częścią tworzonego przeze mnie projektu. Jeśli zaciekawił Cię temat validacji, to zapraszam Cię do sprawdzenia mojego repozytorium na GitHub’ie.

Jeśli coś jest niezrozumiałe lub widzisz błąd, to daj mi o tym znać w komentarzu! Zachęcam też do zapoznania się ze źródłami i materiałami dodatkowymi.

Źródła i materiały dodatkowe

Dominik Szczepaniak

Zawodowo Senior Software Engineer w CKSource. Prywatnie bloger, fan włoskiej kuchni, miłośnik jazdy na rowerze i treningu siłowego.

Inne wpisy, które mogą Cię zainteresować

Zapisz się na mailing i odbierz e-booka

Zapisując się na mój mailing, otrzymasz darmowy egzemplarz e-booka 106 Pytań Rekrutacyjnych Junior JavaScript Developer! Będziesz też otrzymywać wartościowe treści i powiadomienia o nowych wpisach na skrzynkę e-mail.

Subscribe
Powiadom o
guest

5 komentarzy
oceniany
najnowszy najstarszy
Inline Feedbacks
View all comments
Iwan
Iwan
7 miesięcy temu

Rozumiem, że w obecnej wersji express-validator już nie „sanitizeBody”?

ani
ani
4 lat temu

a czy mogę użyć check(’text’).replace(’>’, ”), tak żeby mi sprawdził txt czy sa < i pozamieniał je?

Dominik Szczepaniak
4 lat temu
Reply to  ani

Osobiście skorzystałbym raczej z sanitizeBody('text').escape()

ani
ani
4 lat temu

dzięki 🙂