Вступление
При создании приложений – особенно приложений, обращенных к клиенту, – крайне важно проводить проверку на стороне сервера. Причина в том, что никогда нельзя полагаться только на ввод данных пользователем, поскольку эти данные иногда содержат фиктивные/вредоносные данные.
Проверка на стороне клиента – отличный способ отсеять большую часть вводимых данных, но все же необходимо выполнять проверку и на стороне сервера.
Существует множество способов проверки данных в Node.js, и в этой статье мы рассмотрим express-validator. Express-validator – это библиотека, которая оборачивается вокруг validator.js и раскрывает его функции как набор промежуточных модулей.
Настройка проекта
В этом учебном пособии мы создадим демонстрационный backend-сервер для имитации регистрации и входа пользователей с помощью Node.js. В этих полях будут применяться определенные правила, и мы будем проверять данные, которые приходят через них.
Обратите внимание, что мы не будем обрабатывать реальную логику регистрации и входа пользователей, т.е. сохранять их данные и реализовывать аутентификацию, поскольку это выходит за рамки данной статьи.
Для начала мы создадим папку проекта, перейдем в нее и инициализируем ее:
# Create the project folder $ mkdir express-validator-tut # Navigate into the project folder $ cd express-validator-tut # Initialize project $ yarn init -y # OR $ npm init -y
После этого мы установим следующие зависимости, выполнив приведенную ниже команду:
$ yarn add body-parser express express-validator # OR $ npm i body-parser express express-validator
Давайте посмотрим, что мы установили:
- express: Легкий фреймворк веб-приложений для Node.js. Мы будем использовать его для обработки маршрутизации на нашем внутреннем сервере.
- body-parser: Промежуточное ПО, которое поможет нам разобрать входящие данные запроса (пользовательские данные) в объект req.body.
- express-validator: Библиотека, которую мы будем использовать для обработки валидации входящих данных.
Наконец, мы создадим файл index.js в директории нашего проекта, чтобы разместить в нем код для инстанцирования приложения/сервера Express:
// index.js const express = require('express'); const app = express(); const bodyParser = require('body-parser'); const port = 2022; app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); app.post('/register', (req, res) => {}); app.listen(port); console.log('See where it all happens at http://localhost:'+port);
ТеперТеперь давайте запустим это приложение с помощью node:
$ node index.js
Если все идет хорошо, ваш терминал должен вывести что-то вроде:
Стандартные правила валидации с помощью express-validator
В этом разделе мы узнаем, как добавить простые правила валидации и санации к входящим запросам. Во-первых, мы хотим проверить, является ли значение, введенное в поле email, действительным email или нет. Затем мы хотим убедиться, что пароль содержит не менее 6 символов.
Чтобы начать, давайте добавим пару промежуточных функций в наш маршрут /login:
// index.js ... const { body, validationResult } = require('express-validator'); app.post('/login', body('email').isEmail().normalizeEmail(), body('password').isLength({ min: 6 }), (req, res) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ success: false, errors: errors.array() }); } res.status(200).json({ success: true, message: 'Login successful', }) }); ...
В приведенном выше фрагменте мы используем два метода валидатора:
- isEmail(): Эта функция валидатора проверяет, является ли входящая строка действительным адресом электронной почты.
- isLength(): Этот валидатор проверяет, попадает ли длина строки в указанный диапазон. В нашем случае указанный диапазон – это минимум 6 символов.
Некоторые из других методов, которые мы могли бы использовать, следующие:
- isNumeric() – Проверяет, является ли вводимое значение числовым
- contains() – Проверяет, содержит ли вход определенное значение
- isBoolean() – Проверяет, является ли входное значение булевым.
- isCurrency() – Проверяет, отформатирован ли входной сигнал в валюте
- isJSON() – Проверяет, является ли вводимое значение JSON
- isMobilePhone() – Проверяет, является ли вводимое значение действительным номером мобильного телефона
- isPostalCode() – Проверяет, является ли вводимый номер действительным почтовым индексом.
- isBefore() и isAfter() – Проверяет, находится ли дата до или после другой даты.
Существуют и другие, но эти, вероятно, покроют большинство ваших потребностей в валидации.
Чтобы убедиться, что в адресах электронной почты, вводимых пользователем, нет шумов и неровностей, мы добавим в поле электронной почты санитайзер, как показано в приведенном выше фрагменте. Метод normalizeEmail() помогает преобразовать введенные электронные адреса в стандартный утвержденный формат. Это означает, что если пользователь вводит, например, username@googlemail.com, он будет канонизирован в username@gmail.com.
Validator.js предлагает некоторую гибкость, поскольку этот параметр можно выключить или включить, но по умолчанию он включен. Существует множество опций для нормализации, которые вы, возможно, захотите проверить, если планируете нормализовать входные данные. Если вы хотите прочитать больше о других функциях валидаторов/санитаров, вы можете ознакомиться с официальной документацией Validator.js.
Давайте протестируем наш код, отправив запрос с недействительным паролем и электронным адресом @googleemail.com, используя Postman или curl:
Итак, мы рассмотрели, как можно проверить входящие данные для конечной точки входа в систему. Теперь перейдем к конечной точке регистрации и рассмотрим такие задачи, как пользовательские правила проверки, сообщения об ошибках, проверка схемы и стандартизация сообщений о проверке.
Пользовательские правила валидации и сообщения об ошибках с помощью express-validator
Чтобы начать работу, давайте создадим конечную точку регистрации пользователя, добавив следующий фрагмент в файл index.js:
// index.js ... app.post('/register', (req, res) => { // Validate incoming input res.status(200).json({ success: true, message: 'Registration successful', }); }); ...
Метод custom()
Чтобы убедиться, что наши пользователи вводят уникальные имена пользователей при регистрации, мы не можем использовать стандартные методы, обернутые из методов Validator.js, поскольку не существует метода для их проверки.
Для этого нам придется написать собственный валидатор, что можно сделать с помощью метода custom(). Метод custom() принимает функцию, которая дополнительно может быть async. Если функция async, вы захотите отклонить обещание, если валидация не прошла, и указать пользовательское сообщение. В противном случае вы можете выбросить исключение.
Давайте сначала начнем с отклонения обещания:
// index.js ... app.post('/register', body("username").custom(value => { return User.find({ username: value }).then(user => { if (user.length > 0) { // Custom error message and reject // the promise return Promise.reject('Username already in use'); } }); }), (req, res) => { // Validate incoming input const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); } ... })
В приведенном выше фрагменте кода мы вызываем метод find() на схеме User model Mongoose, чтобы проверить, существует ли уже в нашей базе данных имя пользователя, введенное клиентом.
Если оно существует, мы отклоняем обещание с сообщением, которое мы хотели бы вернуть пользователю.
Хотя MongoDB автоматически обнаружит это, если при задании схемы базы данных поле username было помечено как уникальное. Желательно обработать эту ситуацию до того, как она попадет в БД, чтобы наше приложение не потерпело преждевременного краха.
В качестве альтернативы вы можете выбросить исключение, чтобы обозначить недопустимый ввод:
// index.js ... app.post('/register', body("username").custom(value => { return User.find({ username: value }).then(user => { if (user.length > 0) { throw ("Username is taken!"); //custom error message } }); }), ...
Метод withMessage()
Второй способ реализации пользовательских сообщений об ошибках валидации – это использование цепочки withMessage()-методов. Вы можете разместить несколько валидаторов, а затем цепочку методов withMessage(), чтобы указать сообщения об ошибках для каждой валидации:
body("parameter") .validator1() .withMessage('Message 1') .validator2() .withMessage('Message 2')
Давайте применим это с помощью реальных методов к нашему примеру:
// index.js ... app.post('/register', body("password").isStrongPassword({ minLength: 8, minLowercase: 1, minUppercase: 1, minNumbers: 1 }) .withMessage("Password must be greater than 8 and contain at least one uppercase letter, one lowercase letter, and one number"), (req, res) => { // Validate incoming input }) ...
Давайте сделаем еще один запрос, с неверным паролем и именем пользователя, которое уже используется:
Валидация схем с помощью express-validator
Валидация схемы предлагает более чистый подход к проверке данных. Вместо вызова многочисленных функций мы задаем правила валидации для каждого поля и передаем схему в одну промежуточную функцию checkSchema().
В приведенном ниже фрагменте мы создадим схему валидации для конечной точки регистрации пользователя:
// index.js ... const {body, checkSchema, validationResult} = require('express-validator'); const registrationSchema = { username: { custom: { options: value => { return User.find({ username: value }).then(user => { if (user.length > 0) { return Promise.reject('Username already in use') } }) } } }, gender: { notEmpty: true, errorMessage: "Gender field cannot be empty" }, password: { isStrongPassword: { minLength: 8, minLowercase: 1, minUppercase: 1, minNumbers: 1 }, errorMessage: "Password must be greater than 8 and contain at least one uppercase letter, one lowercase letter, and one number", }, phone: { notEmpty: true, errorMessage: "Phone number cannot be empty" }, email: { normalizeEmail: true, custom: { options: value => { return User.find({ email: value }).then(user => { if (user.length > 0) { return Promise.reject('Email address already taken') } }) } } } } ...
Указав схему, мы можем проанализировать конкретные поля ввода для применения валидаторов и санитаров, и это гораздо удобнее для чтения, чем цепочка из множества методов с сообщениями о проверке, как мы видели в предыдущих разделах.
Теперь мы можем продолжить и использовать эту checkSchema() для проверки данных при регистрации:
app.post('/signup', checkSchema(registrationSchema), (req, res) => { // Validate incoming input const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); } res.status(200).json({ success: true, message: 'Registration successful', }); })
Если вам нужно только небольшое количество валидаций и вы хотите сохранить простоту, вы можете использовать методы. Если вам нужно выполнить огромное количество валидаций, то все будет более читабельно, если вы используете валидацию схемы.
Стандартизация ответов на валидацию с помощью express-validator
express-validator позволяет стандартизировать ответы на ошибки валидации. Это означает, что вы можете создавать свои промежуточные функции для выполнения валидации и обработки ошибок валидации.
Примером того, как это можно сделать, является создание функции validate(), которая будет принимать все наши валидаторы и запускать их параллельно с помощью Promise.all():
// index.js const validate = validations => { return async (req, res, next) => { await Promise.all(validations.map(validation => validation.run(req))); const errors = validationResult(req); if (errors.isEmpty()) { return next(); } res.status(400).json({ errors: errors.array() }); }; }
Теперь наша функция validate создана, и мы можем повторно использовать ее в нескольких маршрутах. Давайте применим ее к нашим маршрутам входа и регистрации:
// index.js ... app.post('/login', validate([ body('email').isEmail().normalizeEmail(), body('password').isLength({ min: 12 }) ]), (req, res) => { // Process data res.status(200).json({ success: true, message: 'Login successful', }) }); app.post('/register', validate(checkSchema(registrationSchema)), (req, res) => { // Process data res.status(200).json({ success: true, message: 'Registration successful', }); }); ...
Как видно из приведенного выше фрагмента, использование пользовательского промежуточного ПО валидации, которое запускает все наши валидаторы и санитары, не только повышает производительность благодаря вызову Promise.all(), но и улучшает читаемость кода. Это окажется полезным, когда нам нужно будет проверять большое количество полей формы.
Заключение
В этой статье мы рассмотрели базовые и более продвинутые возможности использования express-validator, отличной легковесной библиотеки, которая опирается на известную библиотеку validator.js.
Оригинал: “https://stackabuse.com/form-data-validation-in-nodejs-with-express-validator/”