Короткий ответ
Нужно вернуть статус 400 или 422 с машиночитаемым телом ответа, описывающим, какое поле невалидно и почему, не выбрасывая необработанное исключение и не возвращая 500.
Как это работает подробнее
422 Unprocessable Entity означает, что запрос синтаксически верен, но данные не прошли бизнес-валидацию (например, email в неверном формате); 400 Bad Request — более общий код для некорректного запроса в целом. Многие фреймворки (FastAPI с Pydantic) автоматически возвращают 422 при ошибке валидации схемы.
Пример кода
example.py
from pydantic import BaseModel, EmailStr, field_validator
from fastapi import FastAPI
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
app = FastAPI()
class CreateUser(BaseModel):
email: EmailStr
age: int
@field_validator("age")
@classmethod
def age_must_be_positive(cls, v):
if v <= 0:
raise ValueError("age must be positive")
return v
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
return JSONResponse(status_code=422, content={"errors": exc.errors()})
@app.post("/users")
def create_user(payload: CreateUser):
return {"ok": True}Что использовать на практике
- Рекомендация. тело ответа об ошибке стоит делать консистентным по структуре для всего API (например,
{"detail": [...]}или{"errors": [{"field": ..., "message": ...}]}), чтобы клиент мог программно обрабатывать ошибки, а не парсить произвольный текст.
Подводные камни
- Риски. возвращать 500 при ошибке валидации, из-за необработанного исключения типа
ValueErrorвнутри обработчика; возвращать 200 с полем"success": falseвместо честного кода ошибки; не указывать, какое именно поле невалидно, вынуждая клиента угадывать причину.