Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Виды проверок данных (Data Validation)
Проверка данных — критичный процесс для обеспечения качества, безопасности и корректности приложения. Проверки могут происходить на разных уровнях архитектуры.
1. Клиентская валидация (Frontend)
Проверяется в браузере ещё до отправки на сервер.
<!-- HTML5 встроенная валидация -->
<form>
<!-- Обязательное поле -->
<input type="text" name="name" required>
<!-- Email валидация -->
<input type="email" name="email" required>
<!-- Числовое поле с диапазоном -->
<input type="number" name="age" min="18" max="120" required>
<!-- Паттерн -->
<input type="text" name="phone" pattern="[0-9]{10}" placeholder="10 цифр">
<!-- Минимальная длина -->
<input type="password" name="password" minlength="8" required>
</form>
JavaScript валидация:
// Простая валидация
function validateEmail(email) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
}
// Более строгая валидация
const isValidEmail = (email) => {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) &&
email.length <= 254;
};
// Валидация формы
function validateForm(formData) {
const errors = {};
if (!formData.name || formData.name.trim().length < 2) {
errors.name = 'Имя должно быть минимум 2 символа';
}
if (!isValidEmail(formData.email)) {
errors.email = 'Невалидный email';
}
if (formData.password.length < 8) {
errors.password = 'Пароль минимум 8 символов';
}
return Object.keys(errors).length === 0 ? null : errors;
}
Недостаток: клиентскую валидацию легко обойти
2. Серверная валидация (Backend)
Обязательна! Клиентская валидация может быть отключена.
# Базовая валидация в Python
def validate_user_input(data):
errors = {}
if not data.get('email'):
errors['email'] = 'Email обязателен'
elif not '@' in data['email']:
errors['email'] = 'Невалидный email'
if not data.get('password'):
errors['password'] = 'Пароль обязателен'
elif len(data['password']) < 8:
errors['password'] = 'Пароль минимум 8 символов'
if data.get('age'):
try:
age = int(data['age'])
if age < 18 or age > 120:
errors['age'] = 'Возраст должен быть от 18 до 120'
except ValueError:
errors['age'] = 'Возраст должен быть числом'
return errors if errors else None
3. Валидация типов данных (Type Validation)
Проверка что данные правильного типа.
# Встроенная проверка типов в Python
def process_user(name: str, age: int) -> dict:
if not isinstance(name, str):
raise TypeError(f'name должна быть str, получен {type(name)}')
if not isinstance(age, int):
raise TypeError(f'age должна быть int, получена {type(age)}')
return {'name': name, 'age': age}
# Pydantic — лучший инструмент для валидации
from pydantic import BaseModel, EmailStr, Field
class User(BaseModel):
name: str = Field(..., min_length=2, max_length=100)
email: EmailStr # Встроенная валидация email
age: int = Field(..., ge=18, le=120) # greater_equal, less_equal
password: str = Field(..., min_length=8)
class Config:
strict = True
# Использование
try:
user = User(
name='John',
email='john@example.com',
age=25,
password='securepass123'
)
print(user) # Валидные данные
except Exception as e:
print(f'Ошибка валидации: {e}')
# Невалидные данные
try:
user = User(
name='J', # Слишком короткое
email='invalid-email', # Невалидный email
age=15, # Меньше 18
password='short' # Меньше 8 символов
)
except Exception as e:
print(f'Ошибка: {e}')
4. Валидация на уровне БД (Database Constraints)
Проверка непосредственно при сохранении в БД.
-- NOT NULL constraint
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL,
name VARCHAR(100) NOT NULL
);
-- UNIQUE constraint
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
username VARCHAR(50) UNIQUE NOT NULL
);
-- CHECK constraint
CREATE TABLE users (
id SERIAL PRIMARY KEY,
age INT CHECK (age >= 18 AND age <= 120),
salary DECIMAL CHECK (salary >= 0)
);
-- DEFAULT value
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
status VARCHAR(20) DEFAULT 'draft'
);
-- Foreign Key constraint
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
В SQLAlchemy:
from sqlalchemy import Column, Integer, String, CheckConstraint, UniqueConstraint
from sqlalchemy.orm import declarative_base
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
email = Column(String(255), unique=True, nullable=False)
age = Column(Integer, CheckConstraint('age >= 18 AND age <= 120'))
salary = Column(Integer, CheckConstraint('salary >= 0'))
# Составной уникальный индекс
__table_args__ = (
UniqueConstraint('email', 'username', name='uq_email_username'),
)
5. Валидация формата (Format Validation)
Проверка формата строк (email, URL, номер телефона и т.д.).
import re
from email_validator import validate_email, EmailNotValidError
# Email валидация
def is_valid_email(email):
try:
# Используй email_validator для правильной валидации
validate_email(email, check_deliverability=False)
return True
except EmailNotValidError:
return False
# URL валидация
url_pattern = r'^https?:\/\/.+\..+'
url = 'https://example.com'
if re.match(url_pattern, url):
print('Валидный URL')
# Номер телефона
phone_pattern = r'^\+?1?\d{9,15}$'
phone = '+79999999999'
if re.match(phone_pattern, phone):
print('Валидный номер')
# Credit card
card_pattern = r'^\d{13,19}$' # Упрощённый паттерн
# UUID
import uuid
try:
uuid.UUID('550e8400-e29b-41d4-a716-446655440000')
print('Валидный UUID')
except ValueError:
print('Невалидный UUID')
# Pydantic validators
from pydantic import BaseModel, validator
class Article(BaseModel):
url: str
slug: str
@validator('url')
def validate_url(cls, v):
if not v.startswith(('http://', 'https://')):
raise ValueError('URL должен начинаться с http:// или https://')
return v
@validator('slug')
def validate_slug(cls, v):
if not re.match(r'^[a-z0-9-]+$', v):
raise ValueError('Slug содержит недопустимые символы')
return v
6. Валидация диапазонов (Range Validation)
Проверка что значение находится в допустимом диапазоне.
# Числовой диапазон
def validate_age(age):
if not isinstance(age, int):
raise TypeError('Возраст должен быть числом')
if age < 0 or age > 150:
raise ValueError('Возраст должен быть от 0 до 150')
return age
# Длина строки
def validate_password_length(password):
if len(password) < 8:
raise ValueError('Пароль минимум 8 символов')
if len(password) > 128:
raise ValueError('Пароль максимум 128 символов')
return password
# Диапазон дат
from datetime import datetime, timedelta
def validate_date_range(date_str):
date = datetime.fromisoformat(date_str)
if date < datetime.now() - timedelta(days=365):
raise ValueError('Дата слишком старая')
if date > datetime.now() + timedelta(days=1):
raise ValueError('Дата в будущем')
return date
7. Кроссфилдовая валидация (Cross-field Validation)
Проверка связи между несколькими полями.
from pydantic import BaseModel, validator, root_validator
class UserRegistration(BaseModel):
password: str
password_confirm: str
birth_date: str
@root_validator
def validate_passwords_match(cls, values):
if values.get('password') != values.get('password_confirm'):
raise ValueError('Пароли не совпадают')
return values
@validator('birth_date')
def validate_age(cls, v, values):
from datetime import datetime
birth = datetime.fromisoformat(v)
age = (datetime.now() - birth).days // 365
if age < 18:
raise ValueError('Вы должны быть старше 18 лет')
return v
8. Санитизация данных (Data Sanitization)
Очистка данных от опасного контента.
# Удаление опасных символов
import bleach
user_input = '<script>alert("XSS")</script>Hello'
cleaned = bleach.clean(user_input)
print(cleaned) # Hello
# SQL injection защита (используй параметризованные запросы)
# Плохо:
query = f"SELECT * FROM users WHERE email = '{email}'"
# Хорошо:
query = "SELECT * FROM users WHERE email = %s"
cursor.execute(query, (email,))
# HTML экранирование
from html import escape
user_name = '<img src=x onerror=alert(1)>'
safe_name = escape(user_name)
print(safe_name) # <img src=x onerror=alert(1)>
# JSON валидация
import json
try:
data = json.loads(user_input)
except json.JSONDecodeError:
raise ValueError('Невалидный JSON')
Полный пример: валидация при создании пользователя
from pydantic import BaseModel, EmailStr, Field, validator
from sqlalchemy.orm import Session
from models import User
class UserCreate(BaseModel):
email: EmailStr
name: str = Field(..., min_length=2, max_length=100)
password: str = Field(..., min_length=8, max_length=128)
age: int = Field(..., ge=18, le=120)
@validator('name')
def name_alphanumeric(cls, v):
if not all(c.isalnum() or c.isspace() for c in v):
raise ValueError('Имя содержит невалидные символы')
return v
def create_user(user_data: UserCreate, db: Session) -> User:
# 1. Pydantic уже провалидировал данные
# 2. Проверяем уникальность в БД
existing = db.query(User).filter(User.email == user_data.email).first()
if existing:
raise ValueError('Email уже зарегистрирован')
# 3. Хешируем пароль перед сохранением
from werkzeug.security import generate_password_hash
hashed_password = generate_password_hash(user_data.password)
# 4. Создаём объект в БД (constraints проверяют ещё раз)
user = User(
email=user_data.email,
name=user_data.name,
password=hashed_password,
age=user_data.age
)
db.add(user)
db.commit()
db.refresh(user)
return user
Выводы
Уровни валидации (от клиента к БД):
- Frontend — быстрая обратная связь
- Backend — обязательна, проверка типов и формата
- Database — последняя линия защиты (constraints)
Лучшие практики:
- Никогда не доверяй только клиентской валидации
- Используй Pydantic для валидации в Python
- Добавляй constraints на уровне БД
- Санитизируй опасные данные
- Всегда логируй попытки невалидных данных
- Пароли хеш перед сохранением
Инструменты:
- Pydantic — валидация в Python
- email-validator — строгая валидация email
- bleach — санитизация HTML
- SQLAlchemy constraints — валидация на уровне БД