После обновления данных в приложении необходимо повторно проверять корректность значений. Первичная валидация при создании объекта или отправке формы не гарантирует, что данные останутся валидными после редактирования, синхронизации, миграции или преобразований.
Библиотека Validator.js позволяет организовать повторную проверку данных как на клиенте, так и на сервере.
Повторная валидация требуется в следующих ситуациях:
Даже если объект ранее считался корректным, после обновления его структура или значения могут нарушить ограничения.
Через npm:
npm install validator
Подключение:
const validator = require('validator');
ESM:
import validator from 'validator';
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 обновляет только часть объекта.
Пример:
{
"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');
}
}
Данные часто изменяются автоматически:
После преобразования данные могут стать невалидными.
let username = ' x ';
username = validator.trim(username);
if (!validator.isLength(username, { min: 2 })) {
console.log('Слишком короткое имя');
}
После удаления пробелов длина изменилась.
let phone = '+7 (777) 111-22-33';
phone = phone.replace(/\D/g, '');
if (!validator.isMobilePhone(phone, 'kk-KZ')) {
console.log('Некорректный номер');
}
Validator.js содержит методы санитизации.
let text = '<script>alert(1)</script>';
text = validator.escape(text);
console.log(text);
Результат:
<script>alert(1)</script>
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
});
После объединения объектов данные могут конфликтовать.
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('Некорректная локаль');
}
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
});
const emails = [
'admin@example.com',
'invalid-email'
];
const invalidEmails = emails.filter(email => {
return !validator.isEmail(email);
});
console.log(invalidEmails);
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);
});
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.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);
}
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);
Ошибка:
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;
}