Как работает валидация в FastAPI
Что проверяют
Понимаете ли вы, что валидацию в FastAPI делает не сам фреймворк, а Pydantic по аннотациям типов, и откуда берётся ответ 422. Следом спрашивают, как написать свой валидатор, как поменять формат ошибки и проверяется ли ответ.
Как отвечать
FastAPI читает сигнатуру эндпоинта и по аннотациям решает, откуда брать каждое значение и как его проверить:
- тело запроса: параметр с типом наследника
BaseModelчитается из JSON и валидируется моделью; - path, query, header, cookie: простые типы проверяются так же, ограничения задаются через
Annotated[int, Query(ge=1, le=100)]илиPath(); - ограничения полей:
Field(gt=0, max_length=50, pattern=...), типы вродеEmailStr,HttpUrl,Literal,UUID; - свои правила: в Pydantic v2 это
@field_validatorдля одного поля и@model_validatorдля проверок между полями, например чтоendпозжеstart; - ответ:
response_modelили аннотация возвращаемого типа прогоняют результат через модель и отрезают лишние поля, так хэш пароля не уйдёт наружу.
Если данные не прошли, поднимается RequestValidationError, и FastAPI отвечает 422 со списком ошибок, где у каждой есть loc, msg и type. Формат меняется своим обработчиком через @app.exception_handler(RequestValidationError). По умолчанию Pydantic работает в нестрогом режиме и приводит типы (строка "5" станет int), строгий включается через strict=True или ConfigDict(strict=True). Ядро валидации в v2 написано на Rust (pydantic-core), поэтому она заметно быстрее, чем в v1.
Где спотыкаются
Пишут is_offer: bool = None. Тип не допускает None, правильно bool | None = None.
Цитируют формат ошибок из Pydantic v1 (value_error.missing, декоратор @validator). В v2 тип ошибки называется missing, а @validator объявлен устаревшим.
Путают 422 и 400: бизнес-проверку делают внутри эндпоинта через raise ValueError и получают 500. Для этого есть HTTPException с нужным статусом, а проверки формата лучше держать в модели.