Проверка после обновления

После обновления данных в приложении необходимо повторно проверять корректность значений. Первичная валидация при создании объекта или отправке формы не гарантирует, что данные останутся валидными после редактирования, синхронизации, миграции или преобразований.

Библиотека Validator.js позволяет организовать повторную проверку данных как на клиенте, так и на сервере.


Причины повторной проверки

Повторная валидация требуется в следующих ситуациях:

  • изменение полей формы;
  • обновление данных пользователя;
  • частичное редактирование объекта;
  • получение данных из внешнего API;
  • синхронизация с базой данных;
  • применение автоматических преобразований;
  • обновление настроек;
  • импорт CSV, JSON и XML;
  • миграция старых данных.

Даже если объект ранее считался корректным, после обновления его структура или значения могут нарушить ограничения.


Установка Validator.js

Через npm:

npm install validator

Подключение:

const validator = require('validator');

ESM:

import validator from 'validator';

Проверка обновлённых email-адресов

Базовая повторная проверка

import validator from 'validator';

function updateEmail(user, newEmail) {
    if (!validator.isEmail(newEmail)) {
        throw new Error('Некорректный email');
    }

    user.email = newEmail;
}

После изменения значения выполняется повторная валидация.


Проверка после нормализации

Во многих случаях данные предварительно изменяются.

function updateEmail(user, email) {
    const normalized = validator.normalizeEmail(email);

    if (!validator.isEmail(normalized)) {
        throw new Error('Некорректный email');
    }

    user.email = normalized;
}

Повторная проверка после загрузки из БД

const email = user.email;

if (!validator.isEmail(email)) {
    console.log('Повреждённый email в базе');
}

Подобная практика особенно важна после миграций и ручного редактирования данных.


Повторная проверка после обновления профиля

Исходный объект

const profile = {
    name: 'Alex',
    email: 'alex@example.com',
    age: '25'
};

Обновление данных

profile.name = 'A';
profile.age = '-5';

Повторная проверка объекта

const errors = [];

if (!validator.isLength(profile.name, { min: 2 })) {
    errors.push('Имя слишком короткое');
}

if (!validator.isInt(profile.age, { min: 0 })) {
    errors.push('Возраст указан неверно');
}

console.log(errors);

Валидация после PATCH-запросов

Особенности PATCH

PATCH обновляет только часть объекта.

Пример:

{
    "email": "new@example.com"
}

или:

{
    "username": "x"
}

После частичного обновления объект необходимо проверить повторно целиком.


Пример

function patchUser(user, patchData) {
    Object.assign(user, patchData);

    validateUser(user);
}

Полная проверка после обновления

function validateUser(user) {
    if (!validator.isEmail(user.email)) {
        throw new Error('Некорректный email');
    }

    if (!validator.isLength(user.username, {
        min: 3,
        max: 20
    })) {
        throw new Error('Некорректный username');
    }
}

Валидация после преобразований

Опасность автоматических преобразований

Данные часто изменяются автоматически:

  • trim;
  • lowercase;
  • escape;
  • replace;
  • normalize;
  • конвертация типов.

После преобразования данные могут стать невалидными.


Пример с trim

let username = ' x ';

username = validator.trim(username);

if (!validator.isLength(username, { min: 2 })) {
    console.log('Слишком короткое имя');
}

После удаления пробелов длина изменилась.


Пример с replace

let phone = '+7 (777) 111-22-33';

phone = phone.replace(/\D/g, '');

if (!validator.isMobilePhone(phone, 'kk-KZ')) {
    console.log('Некорректный номер');
}

Повторная проверка после sanitize

Validator.js содержит методы санитизации.

Escape

let text = '<script>alert(1)</script>';

text = validator.escape(text);

console.log(text);

Результат:

&lt;script&gt;alert(1)&lt;/script&gt;

Проверка длины после escape

if (!validator.isLength(text, { max: 20 })) {
    console.log('Текст слишком длинный');
}

После экранирования строка становится длиннее.


Валидация после обновления пароля

Повторная проверка сложности

function updatePassword(password) {
    if (!validator.isStrongPassword(password)) {
        throw new Error('Слабый пароль');
    }

    return password;
}

Использование параметров

validator.isStrongPassword(password, {
    minLength: 12,
    minLowercase: 1,
    minUppercase: 1,
    minNumbers: 1,
    minSymbols: 1
});

Валидация после объединения объектов

Проблема merge-операций

После объединения объектов данные могут конфликтовать.

const baseUser = {
    email: 'admin@example.com'
};

const update = {
    email: 'invalid-email'
};

const result = {
    ...baseUser,
    ...update
};

Повторная проверка

if (!validator.isEmail(result.email)) {
    throw new Error('Некорректный email');
}

Проверка после обновления настроек

Пример конфигурации

const settings = {
    theme: 'dark',
    locale: 'ru'
};

Обновление

settings.locale = 'ru-RU';

Повторная проверка

if (!validator.isLocale(settings.locale)) {
    console.log('Некорректная локаль');
}

Повторная проверка URL

После изменения адреса

site.url = 'http:/invalid';

Валидация

if (!validator.isURL(site.url)) {
    console.log('Некорректный URL');
}

Использование параметров

validator.isURL(site.url, {
    protocols: ['https'],
    require_protocol: true
});

Валидация после обновления числовых данных

Повторная проверка цены

product.price = '-150';

Проверка

if (!validator.isFloat(product.price, { min: 0 })) {
    console.log('Цена должна быть положительной');
}

Проверка целых чисел

validator.isInt(quantity, {
    min: 1,
    max: 1000
});

Валидация массивов после обновления

Проверка списка email

const emails = [
    'admin@example.com',
    'invalid-email'
];

Повторная проверка

const invalidEmails = emails.filter(email => {
    return !validator.isEmail(email);
});

console.log(invalidEmails);

Валидация после обновления данных API

Проверка внешних данных

async function loadUser() {
    const response = await fetch('/api/user');
    const data = await response.json();

    if (!validator.isEmail(data.email)) {
        throw new Error('API вернул некорректный email');
    }

    return data;
}

Валидация после миграции данных

Проверка старых записей

for (const user of users) {
    if (!validator.isEmail(user.email)) {
        console.log(user.id);
    }
}

Массовая повторная валидация

const invalidUsers = users.filter(user => {
    return !validator.isEmail(user.email);
});

Валидация после десериализации JSON

Пример

const raw = localStorage.getItem('profile');

const profile = JSON.parse(raw);

Повторная проверка

if (!validator.isJSON(raw)) {
    throw new Error('Некорректный JSON');
}

Проверка структуры

if (!validator.isEmail(profile.email)) {
    throw new Error('Некорректный email');
}

Повторная проверка данных формы

После изменения input

input.addEventListener('change', event => {
    const value = event.target.value;

    if (!validator.isEmail(value)) {
        console.log('Ошибка');
    }
});

Проверка при каждом вводе

input.addEventListener('input', event => {
    const value = event.target.value;

    if (validator.isLength(value, { min: 3 })) {
        console.log('OK');
    }
});

Проверка зависимых полей

После обновления одного поля могут стать невалидными другие.

Пример

const user = {
    password: '12345678',
    confirmPassword: '12345678'
};

Изменение пароля

user.password = 'new-password';

Повторная проверка

if (user.password !== user.confirmPassword) {
    console.log('Пароли не совпадают');
}

Централизованная повторная валидация

Универсальная функция

function validateUser(user) {
    const errors = [];

    if (!validator.isEmail(user.email)) {
        errors.push('Некорректный email');
    }

    if (!validator.isLength(user.username, {
        min: 3,
        max: 20
    })) {
        errors.push('Некорректный username');
    }

    if (!validator.isInt(user.age, {
        min: 0
    })) {
        errors.push('Некорректный возраст');
    }

    return errors;
}

Использование после обновления

Object.assign(user, updates);

const errors = validateUser(user);

if (errors.length > 0) {
    console.log(errors);
}

Повторная валидация в Express

Middleware после обновления

app.put('/user/:id', (req, res) => {
    const data = req.body;

    if (!validator.isEmail(data.email)) {
        return res.status(400).json({
            error: 'Некорректный email'
        });
    }

    res.json({
        success: true
    });
});

Асинхронная повторная проверка

Иногда после обновления требуется комбинировать Validator.js с асинхронными проверками.

Пример

async function validateUser(user) {
    if (!validator.isEmail(user.email)) {
        throw new Error('Некорректный email');
    }

    const exists = await db.users.findByEmail(user.email);

    if (exists) {
        throw new Error('Email уже существует');
    }
}

Частые ошибки при повторной валидации

Проверка только изменённого поля

Ошибка:

patch.email = 'bad-email';

Проверяется только email, но остальные поля объекта могут уже быть повреждены.

Правильный подход:

Object.assign(user, patch);

validateUser(user);

Отсутствие проверки после sanitize

Ошибка:

username = validator.trim(username);

Без повторной проверки длины и формата.


Доверие данным из БД

Наличие записи в базе не означает корректность данных.


Проверка до преобразований

Ошибка:

validator.isEmail(email);

email = validator.normalizeEmail(email);

Правильно:

email = validator.normalizeEmail(email);

validator.isEmail(email);

Архитектура повторной валидации

Подход с отдельным валидатором

class UserValidator {
    static validate(user) {
        const errors = [];

        if (!validator.isEmail(user.email)) {
            errors.push('Некорректный email');
        }

        return errors;
    }
}

Использование

Object.assign(user, updates);

const errors = UserValidator.validate(user);

Повторная валидация и безопасность

Повторная проверка данных предотвращает:

  • повреждение базы данных;
  • сохранение некорректных значений;
  • ошибки бизнес-логики;
  • обход ограничений интерфейса;
  • инъекции через обновлённые данные;
  • нарушение целостности объекта;
  • проблемы совместимости;
  • ошибки сериализации.

Практический пример полного обновления

import validator from 'validator';

function updateUser(user, updates) {
    const updated = {
        ...user,
        ...updates
    };

    updated.email = validator.normalizeEmail(updated.email);

    const errors = [];

    if (!validator.isEmail(updated.email)) {
        errors.push('Некорректный email');
    }

    if (!validator.isLength(updated.username, {
        min: 3,
        max: 20
    })) {
        errors.push('Некорректный username');
    }

    if (!validator.isInt(String(updated.age), {
        min: 0,
        max: 120
    })) {
        errors.push('Некорректный возраст');
    }

    if (errors.length > 0) {
        throw new Error(errors.join(', '));
    }

    return updated;
}