Как в Python реализовать валидацию данных и обработку ошибок на стороне бэкенда

Ответ

Для реализации надежной валидации данных и обработки ошибок на бэкенде используются специализированные библиотеки, которые отделяют логику проверки от основной бизнес-логики. Это обеспечивает чистоту кода и предсказуемое поведение API.

Популярные инструменты:

  1. Pydantic (особенно в FastAPI) Pydantic использует аннотации типов Python для определения схем данных. FastAPI автоматически интегрируется с Pydantic, выполняя валидацию "из коробки" и возвращая стандартизированные ошибки.

    # Используется в FastAPI
    from fastapi import FastAPI
    from pydantic import BaseModel, EmailStr, Field
    
    app = FastAPI()
    
    class UserCreate(BaseModel):
        email: EmailStr
        age: int = Field(gt=18, description="Возраст должен быть больше 18")
    
    @app.post("/users/")
    def create_user(user: UserCreate):
        return {"status": "ok", "email": user.email}
    
    # При неверных данных FastAPI автоматически вернет 422 ошибку с JSON
    # {"detail":[{"loc":["body","age"],"msg":"ensure this value is greater than 18", ...}]}
  2. Marshmallow Популярная библиотека для сериализации/десериализации и валидации объектов. Часто используется с Flask и Django.

    from marshmallow import Schema, fields, ValidationError
    
    class UserSchema(Schema):
        email = fields.Email(required=True)
        age = fields.Int(validate=lambda n: n > 18)
    
    def create_user_view(request_data):
        try:
            data = UserSchema().load(request_data)
            # ... логика создания пользователя
            return {"status": "ok"}, 201
        except ValidationError as err:
            # Возвращаем словарь с ошибками для каждого поля
            return {"errors": err.messages}, 400

Ключевые принципы:

  • Стандартизированный формат ошибок: API должен всегда возвращать ошибки в единой структуре (например, {"errors": {"field_name": ["error message"]}}), чтобы клиент мог их единообразно обрабатывать.
  • Корректные HTTP-статусы: Используйте 400 Bad Request для общих ошибок запроса и 422 Unprocessable Entity для ошибок валидации данных.
  • Четкие сообщения об ошибках: Сообщения должны быть понятны разработчику клиента (а иногда и конечному пользователю).
  • Разделение логики: Логика валидации должна быть инкапсулирована в схемах или классах данных, а не разбросана по коду обработчиков запросов.

Ответ 18+ 🔞

А, ну вот, опять про валидацию на бэкенде заговорили. Слушай, а это ж святое дело, блядь! Это как, понимаешь, ворота на въезде в крепость — если их нихуя не охранять, то всякие распиздяи с кривыми данными тебе всю систему засрут, в рот меня чих-пых!

Так вот, чтобы не было как в том анекдоте — «пришёл запрос, а там вместо возраста — "старый как говно мамонта"» — умные дядьки придумали целые библиотеки. Это не просто код, это, блядь, такие надзиратели с дубинками, которые каждую входящую цифру или букву проверяют на вшивость.

Ну, инструменты, которые в ходу:

  1. Pydantic (особенно в FastAPI) Это вообще, ёпта, магия какая-то. Ты просто пишешь, как данные должны выглядеть, а он сам всё проверяет. FastAPI на него подсел так, что они теперь как сиамские близнецы, блядь. Сделал ошибку — получай красивую ошибку обратно, а не «всё упало, иди ищи, мудак».

    # Используется в FastAPI
    from fastapi import FastAPI
    from pydantic import BaseModel, EmailStr, Field
    
    app = FastAPI()
    
    class UserCreate(BaseModel):
        email: EmailStr  # Смотри, сука, не просто строка, а именно имейл!
        age: int = Field(gt=18, description="Возраст должен быть больше 18")  # А тут прямо сказано: если меньше 18 — иди нахуй!
    
    @app.post("/users/")
    def create_user(user: UserCreate):
        return {"status": "ok", "email": user.email}
    
    # При неверных данных FastAPI автоматически вернет 422 ошибку с JSON
    # {"detail":[{"loc":["body","age"],"msg":"ensure this value is greater than 18", ...}]}
  2. Marshmallow А это, блядь, классика жанра, как Лев Толстой. Часто с Flask или Django вяжут. Тут ты сам, как режиссёр, описываешь схему: это поле — имейл, это — число, и чтобы больше 18 было, а то я тебе сейчас в ухо нассу!

    from marshmallow import Schema, fields, ValidationError
    
    class UserSchema(Schema):
        email = fields.Email(required=True)  # required=True — это значит «обязательно, блядь, а то получишь по ебалу!»
        age = fields.Int(validate=lambda n: n > 18)  # Лямбду свою туда же, проверяй что хочешь!
    
    def create_user_view(request_data):
        try:
            data = UserSchema().load(request_data)  # Пробуем загрузить
            # ... логика создания пользователя
            return {"status": "ok"}, 201
        except ValidationError as err:
            # А тут — бац! — поймали ошибку и отдаём аккуратно, как в сортире
            return {"errors": err.messages}, 400

А теперь, блядь, главные правила, чтобы не быть мудаком:

  • Ошибки — одним форматом. Представь, ты приходишь в кабак, а тебе то «иди нахуй» скажут, то «пошёл в пизду», то «ваш запрос не валиден». Заебёшься понимать. Так и клиент. Один формат, например {"errors": {"поле": ["сообщение"]}} — и всем понятно.
  • Статусы HTTP — не просто цифры. 400 — это «ты, братан, хуйню прислал». 422 — это «данные-то вроде есть, но они, блядь, кривые как рога у чёрта». Используй правильно, это как спецсигналы.
  • Сообщения — чтоб понятно было. Не «validation error», а «возраст должен быть числом, больше 18, а не "молодой и перспективный"».
  • Логика валидации — отдельно. Это как не мешать борщ с компотом. Вся проверка — в схемах или специальных классах. А в основном коде — чистая бизнес-логика: создать, сохранить, отправить ответ. Красота, ёпта!

Вот так, блядь. Сделаешь по этим правилам — и клиенты тебя любить будут, и система не развалится от первого же долбоёба, который решит в поле «пароль» отправить <script>alert('лох')</script>.