Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Уровни валидации
Валидация — проверка корректности данных на разных уровнях приложения. Это критично для безопасности и надёжности.
1. Клиентская валидация (Frontend)
Первый уровень — немедленная обратная связь пользователю.
# Пример с использованием JavaScript (но показываю принцип)
# На фронте валидируем перед отправкой
if not email.includes('@'):
show_error('Invalid email')
else:
send_to_backend()
Преимущества: быстрая обратная связь, экономия трафика.
Недостатки: клиент может быть скомпрометирован, легко обойти.
2. API валидация (Server-Side)
Проверка входных данных на сервере — обязательна, так как клиент ненадёжен.
2.1 Валидация типов
from fastapi import FastAPI
from pydantic import BaseModel, Field, EmailStr
app = FastAPI()
class UserCreate(BaseModel):
name: str = Field(..., min_length=1, max_length=100)
email: EmailStr
age: int = Field(..., ge=18, le=150)
phone: str | None = None
@app.post('/users')
async def create_user(user: UserCreate):
# Pydantic автоматически валидирует типы
return {'user': user}
Pydantic проверяет:
- Тип данных (str, int, email и т.д.)
- Диапазоны (ge, le, min_length, max_length)
- Формат (EmailStr, HttpUrl, UUID)
- Кастомные правила
2.2 Бизнес-логика валидация
from pydantic import BaseModel, validator, root_validator
class UserCreate(BaseModel):
username: str
password: str
password_confirm: str
@validator('password')
def validate_password(cls, v):
if len(v) < 8:
raise ValueError('Password must be 8+ characters')
if not any(c.isupper() for c in v):
raise ValueError('Password must contain uppercase')
return v
@root_validator()
def validate_passwords_match(cls, values):
if values.get('password') != values.get('password_confirm'):
raise ValueError('Passwords do not match')
return values
# Использование
try:
user = UserCreate(
username='john',
password='SecurePass123',
password_confirm='SecurePass123'
)
except ValueError as e:
print(f'Validation error: {e}')
3. Валидация БД (Database)
Констрейнты на уровне БД — последняя защита от некорректных данных.
from sqlalchemy import Column, String, Integer, 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)
username = Column(String(100), nullable=False)
age = Column(
Integer,
CheckConstraint('age >= 18 AND age <= 150'),
nullable=False
)
__table_args__ = (
UniqueConstraint('username', name='uq_username'),
)
Соответствующая миграция (Goose):
-- migrations/0001_create_users.sql
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
username VARCHAR(100) NOT NULL,
age INTEGER CHECK (age >= 18 AND age <= 150) NOT NULL,
CONSTRAINT uq_username UNIQUE (username)
);
4. Валидация бизнес-правил
Проверка логики, которая требует доступа к БД.
from sqlalchemy.ext.asyncio import AsyncSession
class UserService:
@staticmethod
async def create_user(
session: AsyncSession,
data: UserCreate
) -> User:
# Проверить уникальность email
existing = await session.execute(
select(User).where(User.email == data.email)
)
if existing.scalar_one_or_none():
raise ValueError('Email already registered')
# Проверить бизнес-правила
if data.age < 18:
raise ValueError('Must be 18+')
# Создать пользователя
user = User(
email=data.email,
username=data.username,
age=data.age
)
session.add(user)
await session.commit()
return user
5. Валидация аутентификации и авторизации
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPCredentials
class SecurityService:
@staticmethod
async def verify_token(credentials: HTTPCredentials) -> dict:
try:
payload = jwt.decode(
credentials.credentials,
SECRET_KEY,
algorithms=['HS256']
)
user_id: str = payload.get('sub')
if user_id is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail='Invalid token'
)
return {'user_id': user_id}
except jwt.InvalidTokenError:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail='Invalid token'
)
security = HTTPBearer()
@app.post('/protected')
async def protected_route(auth=Depends(security)):
user_data = await SecurityService.verify_token(auth)
return user_data
6. Валидация безопасности
import re
from typing import Annotated
class SanitizationService:
@staticmethod
def sanitize_html(content: str) -> str:
# Удалить потенциально опасные теги
dangerous_patterns = [
r'<script[^>]*>.*?</script>',
r'javascript:',
r'on\w+\s*='
]
sanitized = content
for pattern in dangerous_patterns:
sanitized = re.sub(pattern, '', sanitized, flags=re.IGNORECASE)
return sanitized
@staticmethod
def validate_sql_injection(query: str) -> bool:
# Использовать параметризованные запросы вместо конкатенации
if "'" in query or '"' in query:
# Warning: может быть false positive
pass
return True
# Всегда используй параметризованные запросы
from sqlalchemy import text
async def get_user(session: AsyncSession, user_id: int):
# ✅ Правильно: параметризованный запрос
result = await session.execute(
text('SELECT * FROM users WHERE id = :id'),
{'id': user_id}
)
return result.scalar_one_or_none()
Иерархия валидации
- Frontend — быстрая обратная связь (не обязательна)
- API (Pydantic) — валидация типов и формата
- Service слой — бизнес-логика валидация
- Database — constrain и уникальность
- Security — аутентификация, авторизация, санитизация
Валидация на каждом уровне должна быть независимой — не полагайся на валидацию на предыдущем уровне.