Middleware-подход в валидации строится вокруг идеи последовательной обработки данных через цепочку независимых функций, каждая из которых выполняет строго ограниченную задачу: проверку, преобразование или обогащение входных данных. В контексте использования библиотеки Validator.js этот паттерн позволяет превратить набор разрозненных проверок в управляемый, расширяемый и легко тестируемый конвейер.
Каждый middleware представляет собой функцию с единым интерфейсом:
Типовая сигнатура:
function middleware(ctx, next) {
// работа с ctx
return next();
}
ctx — объект контекста, содержащий данные для валидации
next — функция перехода к следующему этапу цепочки
Валидация с Validator.js часто строится поверх таких цепочек, где каждый middleware вызывает одну или несколько функций проверки из библиотеки.
Validator.js предоставляет набор функций для проверки строковых значений: email, URL, длина, числовые диапазоны и другие. Сам по себе он не реализует middleware-подход, но хорошо в него встраивается.
Пример использования:
import validator from 'validator';
function isEmailMiddleware(ctx, next) {
if (!validator.isEmail(ctx.value)) {
ctx.errors.push('Некорректный email');
return;
}
return next();
}
Здесь Validator.js выполняет атомарную проверку, а middleware управляет потоком выполнения.
Для организации цепочки используется единый объект контекста:
const ctx = {
value: 'test@example.com',
errors: [],
sanitized: null
};
Middleware работают с этим объектом, не создавая побочных эффектов вне цепочки.
Преимущества:
Цепочка формируется как массив функций:
const middlewares = [
normalizeInput,
validateNotEmpty,
isEmailMiddleware,
sanitizeEmail
];
Исполнитель:
function runMiddlewares(ctx, middlewares) {
let index = 0;
function next() {
const middleware = middlewares[index++];
if (!middleware) return;
return middleware(ctx, next);
}
return next();
}
Такой подход повторяет архитектуру, используемую в серверных фреймворках, но применяется к валидации.
Отвечают за нормализацию данных до проверки:
function trimMiddleware(ctx, next) {
if (typeof ctx.value === 'string') {
ctx.value = ctx.value.trim();
}
return next();
}
Используются до любых проверок Validator.js.
Основной слой логики:
function lengthMiddleware(ctx, next) {
if (!validator.isLength(ctx.value, { min: 5 })) {
ctx.errors.push('Слишком короткое значение');
return;
}
return next();
}
Каждый такой middleware отвечает за одну конкретную проверку.
Прерывают цепочку при критических ошибках:
function requiredMiddleware(ctx, next) {
if (validator.isEmpty(ctx.value || '')) {
ctx.errors.push('Поле обязательно');
return;
}
return next();
}
Используются при необходимости внешних проверок:
async function uniqueEmailMiddleware(ctx, next) {
const exists = await fakeDbCheck(ctx.value);
if (exists) {
ctx.errors.push('Email уже используется');
return;
}
return next();
}
Middleware-движок должен поддерживать Promise:
async function runMiddlewaresAsync(ctx, middlewares) {
let index = 0;
async function next() {
const middleware = middlewares[index++];
if (!middleware) return;
return await middleware(ctx, next);
}
return next();
}
Middleware позволяют собирать сложные правила из простых блоков:
const emailValidationPipeline = [
trimMiddleware,
requiredMiddleware,
isEmailMiddleware,
normalizeEmailMiddleware,
uniqueEmailMiddleware
];
Такой подход заменяет монолитные функции валидации.
Ошибки накапливаются в контексте:
function hasErrors(ctx) {
return ctx.errors.length > 0;
}
Middleware могут учитывать это:
function stopIfErrorsMiddleware(ctx, next) {
if (hasErrors(ctx)) return;
return next();
}
Это позволяет строить условные ветвления без усложнения архитектуры.
Middleware-подход естественно поддерживает модульность. Новые проверки легко добавляются без изменения существующего кода:
function isNumericMiddleware(ctx, next) {
if (!validator.isNumeric(ctx.value)) {
ctx.errors.push('Значение должно быть числом');
return;
}
return next();
}
Добавление:
middlewares.push(isNumericMiddleware);
Один и тот же middleware можно применять к разным схемам валидации:
const passwordPipeline = [
trimMiddleware,
requiredMiddleware,
lengthMiddleware
];
const agePipeline = [
requiredMiddleware,
isNumericMiddleware
];
Это снижает дублирование кода и упрощает сопровождение.
Порядок middleware критичен. Например:
Нарушение порядка приводит к ложным ошибкам или избыточным вычислениям.
Middleware легко масштабируются до работы с объектами:
function fieldMiddleware(field, pipeline) {
return (ctx, next) => {
const fieldCtx = {
value: ctx.data[field],
errors: []
};
return runMiddlewares(fieldCtx, pipeline).then(() => {
ctx.errors[field] = fieldCtx.errors;
return next();
});
};
}
Middleware позволяет оптимизировать выполнение:
Пример:
function skipIfInvalidEmail(ctx, next) {
if (ctx.errors.includes('email')) return;
return next();
}
Middleware-архитектура усложняет:
При чрезмерном использовании цепочки становятся трудно читаемыми, особенно при отсутствии строгих соглашений о структуре ctx.
В масштабных приложениях Validator.js используется только как низкоуровневый слой. Middleware берут на себя:
Таким образом, библиотека становится набором атомарных функций внутри более сложной архитектуры, а не самостоятельной системой валидации.