Пошук уроків, статей та іншого контенту
Навчимося перевіряти типи, формат, довжину й діапазони вхідних даних до виконання бізнес-логіки.
Вхідні дані надходять від користувача, браузера або іншого сервісу. Їх не можна вважати правильними лише тому, що клієнтська форма перевіряла значення.
Валідація допомагає перевірити:
тип даних;
обов’язковість поля;
формат значення;
мінімальну й максимальну довжину;
допустимий діапазон чисел;
відповідність кількох умов одночасно.
Перевіряти дані потрібно до виконання бізнес-логіки. Наприклад, сервер не повинен створювати обліковий запис, поки не переконається, що ім’я, електронна пошта, пароль і вік мають коректні значення.
Валідацію зручно виконувати послідовно.
Якщо сервер отримує JSON, спочатку потрібно перетворити текст на JavaScript-об’єкт за допомогою JSON.parse().
Некоректний JSON не можна обробити як звичайні дані:
const body = JSON.parse('{"name":"Olena"}');JSON.parse() може завершитися помилкою, тому його потрібно викликати в try...catch.
Тип значення можна перевірити за допомогою typeof:
typeof value === 'string';
typeof value === 'number';
typeof value === 'boolean';Для масивів і об’єктів потрібно враховувати особливості JavaScript:
Array.isArray(value);
value !== null && typeof value === 'object';Наприклад, typeof null повертає "object", тому самої перевірки typeof недостатньо.
Для простих форматів можна використовувати регулярні вирази. Наприклад, електронна пошта повинна містити символ @ і домен:
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;Регулярний вираз не гарантує, що адреса справді існує, але допомагає відхилити очевидно некоректні значення.
Для рядків використовують властивість .length:
username.length >= 3 && username.length <= 20;Перед цим потрібно переконатися, що значення справді є рядком.
Для чисел перевіряють не лише тип, а й допустимі межі:
Number.isInteger(age) && age >= 14 && age <= 120;Number.isInteger() також відхилить десяткові числа та значення, які не є числами.
Зазвичай функція валідації не виконує бізнес-логіку. Вона лише повертає результат перевірки та список помилок.
Приклад нижче перевіряє дані для створення облікового запису:
username — рядок довжиною від 3 до 20 символів;
email — рядок у базовому форматі електронної пошти;
password — рядок довжиною від 8 до 64 символів;
age — ціле число від 14 до 120.
function validateRegistrationInput(input) {
const errors = {};
if (
input === null ||
typeof input !== 'object' ||
Array.isArray(input)
) {
return {
valid: false,
errors: {
body: 'Очікується JSON-об’єкт'
}
};
}
const { username, email, password, age } = input;
if (typeof username !== 'string') {
errors.username = 'Ім’я користувача має бути рядком';
} else {
const normalizedUsername = username.trim();
if (normalizedUsername.length < 3 || normalizedUsername.length > 20) {
errors.username = 'Ім’я користувача має містити від 3 до 20 символів';
} else if (!/^[a-zA-Z0-9_]+$/.test(normalizedUsername)) {
errors.username =
'Ім’я користувача може містити лише літери, цифри та символ _';
}
}
if (typeof email !== 'string') {
errors.email = 'Електронна пошта має бути рядком';
} else {
const normalizedEmail = email.trim();
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(normalizedEmail)) {
errors.email = 'Некоректний формат електронної пошти';
}
}
if (typeof password !== 'string') {
errors.password = 'Пароль має бути рядком';
} else if (password.length < 8 || password.length > 64) {
errors.password = 'Пароль має містити від 8 до 64 символів';
}
if (!Number.isInteger(age)) {
errors.age = 'Вік має бути цілим числом';
} else if (age < 14 || age > 120) {
errors.age = 'Вік має бути в діапазоні від 14 до 120 років';
}
return {
valid: Object.keys(errors).length === 0,
errors
};
}
function registerUser(rawBody) {
let input;
try {
input = JSON.parse(rawBody);
} catch {
return {
status: 400,
body: {
error: 'Тіло запиту має містити коректний JSON'
}
};
}
const validation = validateRegistrationInput(input);
if (!validation.valid) {
return {
status: 400,
body: {
error: 'Помилка валідації',
fields: validation.errors
}
};
}
// Бізнес-логіка виконується лише після успішної валідації.
const user = {
username: input.username.trim(),
email: input.email.trim().toLowerCase(),
age: input.age
};
return {
status: 201,
body: {
message: 'Користувача створено',
user
}
};
}
const requests = [
JSON.stringify({
username: 'olena_25',
email: 'olena@example.com',
password: 'secure-password',
age: 25
}),
JSON.stringify({
username: 'ab',
email: 'wrong-email',
password: '123',
age: 150
}),
'{"username": "olena"'
];
for (const request of requests) {
console.log(registerUser(request));
}Цей файл можна зберегти як validation.js і запустити командою:
node validation.jsДля першого запиту валідація буде успішною. Другий запит містить кілька помилок, а третій має некоректний JSON.
Нормалізація — це приведення даних до зручного вигляду. Наприклад:
const username = input.username.trim();
const email = input.email.trim().toLowerCase();Важливо розділяти ці поняття:
валідація визначає, чи можна прийняти значення;
нормалізація змінює представлення значення.
Наприклад, пробіли на початку та в кінці електронної пошти можна прибрати перед перевіркою. Водночас пароль зазвичай не варто автоматично обрізати через trim(), оскільки пробіли можуть бути частиною пароля.
Зручно повертати однакову структуру:
{
valid: false,
errors: {
email: 'Некоректний формат електронної пошти',
age: 'Вік має бути в діапазоні від 14 до 120 років'
}
}Поле valid показує загальний результат, а errors містить помилки окремих полів.
Такий формат дає змогу:
показати користувачу конкретні помилки;
повернути помилки з HTTP-сервера у форматі JSON;
не переривати перевірку після першої помилки;
повторно використовувати функцію валідації в різних частинах програми.
Неправильний порядок дій:
// Спочатку створюємо запис у базі даних.
// Лише потім перевіряємо вхідні дані.Правильний порядок:
Отримати вхідні дані.
Розібрати JSON, якщо він надійшов текстом.
Перевірити структуру та типи.
Перевірити формат, довжину й діапазони.
Повернути помилки, якщо дані некоректні.
Виконати бізнес-логіку лише для валідних даних.
Це захищає програму від помилок і не дає створювати некоректні записи.
Перевірки у браузері покращують взаємодію з користувачем, але не захищають сервер. Клієнт може бути змінений або взагалі відсутній.
Сервер повинен повторно перевіряти всі важливі вхідні дані.
Такої перевірки недостатньо:
if (input.age) {
// ...
}Вона не перевіряє, чи є age числом і чи входить воно в допустимий діапазон.
Краще перевіряти конкретні умови:
if (!Number.isInteger(input.age) || input.age < 14) {
// Дані некоректні
}parseInt() без додаткової перевіркиparseInt() може частково перетворити рядок:
parseInt('25years', 10); // 25Якщо поле повинно містити саме число, краще перевіряти тип отриманого значення за допомогою Number.isInteger() або typeof.
Функція валідації не повинна:
створювати користувача;
записувати дані у файл або базу даних;
надсилати електронні листи;
змінювати стан програми.
Її завдання — перевірити вхідні дані та повернути результат.
Навіть рядок правильного типу може бути надто довгим. Для текстових полів потрібно визначати мінімальну й максимальну довжину.
Повідомлення Невірні дані не пояснює, що саме потрібно виправити. Краще вказувати поле й конкретну умову:
Пароль має містити від 8 до 64 символівВхідним даним із зовнішнього джерела не можна довіряти без перевірки.
JSON потрібно спочатку коректно розібрати за допомогою JSON.parse().
Для кожного поля варто перевіряти тип, формат, довжину та діапазон.
Валідацію потрібно виконувати до бізнес-логіки.
Зручно повертати прапорець успішності та об’єкт помилок.
Нормалізація та валідація — різні операції.
Серверна валідація необхідна навіть тоді, коли дані вже перевірялися на клієнті.