Работа с HTTP-запросами в серверных приложениях на Node.js неизбежно приводит к необходимости строгой проверки входящих данных. Параметры маршрутов, query-string и части URL формируют поверхность атаки, через которую в систему могут попадать некорректные или вредоносные значения. В таких условиях библиотека Validator.js становится базовым инструментом для проверки строковых данных, позволяя стандартизировать подход к валидации и снижать вероятность ошибок на уровне бизнес-логики.
В контексте серверных приложений запрос обычно разделяется на несколько независимых источников данных:
/users/:id?, например ?page=2&limit=10Каждый из этих элементов имеет разные требования к валидации, однако общая логика остаётся одинаковой: входные данные всегда представляют собой строки, которые необходимо проверить на соответствие ожидаемому формату.
Библиотека Validator.js ориентирована на строковую валидацию и предоставляет набор функций для проверки форматов:
isInt, isFloat)isUUID)matches,
isSlug)isBoolean)isURL, isEmail)Ключевой особенностью является то, что Validator.js не работает с объектами или схемами напрямую — он проверяет только строки. Это делает её удобной для использования на уровне контроллеров и middleware, где входные данные уже приведены к строковому виду.
Параметры маршрута часто используются для идентификации ресурсов:
app.get('/users/:id', (req, res) => {
const { id } = req.params;
});
Значение id всегда приходит как строка, даже если
ожидается число. Без проверки это может привести к некорректной работе
бизнес-логики.
import validator fr om 'validator';
app.get('/users/:id', (req, res) => {
const { id } = req.params;
if (!validator.isInt(id, { min: 1 })) {
return res.status(400).json({ error: 'Некорректный идентификатор' });
}
const userId = parseInt(id, 10);
});
Использование isInt позволяет ограничить диапазон
значений и исключить отрицательные числа или нечисловые строки.
Современные API часто используют UUID вместо числовых идентификаторов:
app.get('/orders/:orderId', (req, res) => {
const { orderId } = req.params;
if (!validator.isUUID(orderId)) {
return res.status(400).json({ error: 'Некорректный UUID заказа' });
}
});
UUID обеспечивает более высокий уровень уникальности и снижает предсказуемость идентификаторов.
Query-параметры используются для фильтрации, пагинации и сортировки:
/products?page=2&limit=20&sort=price
Все значения в query-string также представлены строками.
app.get('/products', (req, res) => {
const { page = '1', lim it = '10' } = req.query;
if (!validator.isInt(page, { min: 1 }) ||
!validator.isInt(limit, { min: 1, max: 100 })) {
return res.status(400).json({ error: 'Некорректные параметры пагинации' });
}
const pageNum = parseInt(page, 10);
const limitNum = parseInt(limit, 10);
});
Ограничение max у limit защищает от
чрезмерной нагрузки на сервер.
const { sort } = req.query;
if (sort && !validator.matches(sort, /^(price|name|date)$/)) {
return res.status(400).json({ error: 'Недопустимое значение сортировки' });
}
Регулярные выражения позволяют ограничить набор допустимых значений без усложнения логики.
Иногда часть URL используется как семантический параметр:
/blog/node-js-routing-guide
Здесь node-js-routing-guide является
slug-идентификатором.
app.get('/blog/:slug', (req, res) => {
const { slug } = req.params;
if (!validator.isSlug(slug)) {
return res.status(400).json({ error: 'Некорректный slug' });
}
});
Slug-валидация особенно важна при генерации SEO-дружественных URL.
В реальных приложениях параметры редко существуют изолированно. Часто требуется проверка нескольких значений одновременно:
app.get('/posts/:postId/comments', (req, res) => {
const { postId } = req.params;
const { page = '1' } = req.query;
const errors = [];
if (!validator.isUUID(postId)) {
errors.push('Некорректный ID поста');
}
if (!validator.isInt(page, { min: 1 })) {
errors.push('Некорректная страница');
}
if (errors.length > 0) {
return res.status(400).json({ errors });
}
});
Такой подход позволяет агрегировать ошибки вместо раннего завершения обработки.
Повторяющиеся проверки удобно выносить в middleware:
function validateUserId(req, res, next) {
const { id } = req.params;
if (!validator.isInt(id, { min: 1 })) {
return res.status(400).json({ error: 'Некорректный ID' });
}
next();
}
app.get('/users/:id', validateUserId, (req, res) => {
res.send('OK');
});
Такой подход снижает дублирование кода и повышает консистентность проверок.
Валидация часто сопровождается нормализацией:
const email = validator.normalizeEmail(req.body.email);
Нормализация приводит данные к стандартному виду, что уменьшает вероятность дубликатов и логических ошибок.
Валидация путей напрямую связана с безопасностью:
Некорректные значения могут привести к доступу к файловой системе:
/files/. ./. ./etc/passwd
Проверка:
const { file } = req.params;
if (file.includes('..') || !validator.matches(file, /^[a-zA-Z0-9_-]+$/)) {
return res.status(400).json({ error: 'Недопустимое имя файла' });
}
Даже при использовании ORM или query builder некорректные параметры могут повлиять на логику:
if (!validator.isAlphanumeric(req.query.search || '')) {
return res.status(400).json({ error: 'Недопустимый поисковый запрос' });
}
const allowed = ['asc', 'desc'];
if (!allowed.includes(req.query.order)) {
return res.status(400).json({ error: 'Некорректный порядок сортировки' });
}
if (!validator.isLength(req.params.slug, { min: 3, max: 100 }) ||
!validator.matches(req.params.slug, /^[a-z0-9-]+$/)) {
return res.status(400).json({ error: 'Некорректный slug' });
}
Validator.js не хранит состояние и не зависит от окружения, что делает его удобным для:
Однако его ограничения важно учитывать: библиотека не работает с объектными схемами и требует ручной композиции проверок.
// Ошибка: сравнение строки и числа без проверки
if (req.params.id > 100) {}
const userId = req.params.id;
// использование userId до валидации
Query часто остаётся невалидированным, несмотря на участие в бизнес-логике.
Типичная структура маршрута с полной валидацией:
app.get('/users/:id/posts', (req, res) => {
const { id } = req.params;
const { page, limit } = req.query;
if (!validator.isUUID(id)) {
return res.status(400).json({ error: 'Invalid user ID' });
}
if (!validator.isInt(page || '1', { min: 1 }) ||
!validator.isInt(limit || '10', { min: 1, max: 50 })) {
return res.status(400).json({ error: 'Invalid pagination' });
}
// безопасная бизнес-логика
});
Централизованная обработка позволяет унифицировать ответы API:
function errorResponse(res, messages) {
return res.status(400).json({
status: 'error',
errors: messages
});
}
isInt или
isFloat