Валидация FastAPI: как запретить лишние поля и найти опечатку клиента
Фронтенд отправил в заказ поле quantitty вместо quantity. API ответил 200, но сохранил количество по умолчанию — одну штуку. Опечатка прошла незаметно, потому что неизвестные поля Pydantic по умолчанию игнорирует. Для команды создания заказа это опасный договор: успешный ответ ещё не означает, что сервер понял всё отправленное.
Определите политику входных данных
У Pydantic есть три способа обращения с дополнительными полями: игнорировать, разрешать и запрещать. Выбор зависит от назначения документа. Команда, которая меняет состояние, обычно выигрывает от строгого списка ключей: клиент сразу узнаёт, что прислал неподдерживаемое поле. Событие внешнего поставщика иногда требует более мягкой совместимости, чтобы новое служебное поле не остановило приём.
В модели команды установим ConfigDict(extra="forbid"). Это правило касается названий полей. Оно не включает строгую проверку типов и не задаёт ограничения количества: для этого по-прежнему нужен Field. Полезно разделять эти решения, чтобы понять причину отказа: неизвестный ключ, неверный тип или значение вне допустимых границ.
Проверяем форму команды на HTTP-границе
Модель ниже допускает непустое имя товара и положительное количество, равное единице по умолчанию. Пример намеренно сохраняет этот дефолт: именно рядом с ним опечатка в имени поля особенно легко превращается в незаметную ошибку. Тест сравнивает правильный запрос, опечатку и нулевое количество.
Пример рассчитан на Python 3.11+ с FastAPI, Pydantic v2 и HTTPX. Установите пакеты fastapi и httpx в отдельное окружение, сохраните блок в файл и запустите его Python.
from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel, ConfigDict, Field
app = FastAPI()
class OrderIn(BaseModel):
model_config = ConfigDict(extra="forbid")
product: str = Field(min_length=1)
quantity: int = Field(default=1, ge=1)
@app.post("/orders")
def create_order(data: OrderIn):
return data
with TestClient(app) as client:
assert client.post("/orders", json={"product": "Book"}).json()["quantity"] == 1
bad = client.post("/orders", json={"product": "Book", "quantitty": 3})
assert bad.status_code == 422
error = bad.json()["detail"][0]
assert error["type"] == "extra_forbidden"
assert error["loc"] == ["body", "quantitty"]
assert client.post("/orders", json={"product": "Book", "quantity": 0}).status_code == 422
assert client.post("/orders", json={"product": "Book", "quantity": 3}).json()["quantity"] == 3
print("unknown field rejected; valid quantity preserved")Что сообщить разработчику клиента
В ошибке loc показывает путь до проблемного значения. Для опечатки это body, quantitty, а type равен extra_forbidden. Отдельно зафиксируйте такой ответ в тесте интеграции: интерфейс должен показать ошибку ввода или отправить исправленное имя поля, а не повторять тот же запрос бесконечно. Полный текст сообщения может меняться между версиями, поэтому устойчивее проверять тип и путь.
Не возвращайте клиенту все внутренние исключения через str(exc). FastAPI уже формирует структурированный ответ для ошибки входной модели. Если продукту нужен собственный формат, преобразуйте только нужные элементы в обработчике RequestValidationError. Значения input в такой структуре могут содержать пользовательские данные; учитывайте это перед записью ошибок в журнал.
Как внедрить правило в работающий API
У включения запрета есть цена совместимости. Клиенты, которые годами присылали лишний служебный ключ, начнут получать 422. Перед изменением выясните фактическую форму запросов в тестовых примерах и договорах с клиентами. Согласуйте удаление лишних полей или версию контракта. Делать правило общим базовым классом удобно только после этого решения.
Вложенная модель имеет собственную конфигурацию. Если заказ содержит адрес, extra="forbid" во внешней модели не заменяет аналогичное правило для Address. Проверьте опечатку и снаружи, и внутри вложенного объекта. А публичный ответ настраивается отдельно: существующий разбор фильтрации response_model отвечает за утечку служебных данных, тогда как эта статья защищает смысл входящей команды.
Следующая практика
Продолжите с открытым уроком о POST и первой Pydantic-модели: опишите JSON-команду, получите её в обработчике и проверьте ответ API. Так политика лишних ключей останется частью понятного контракта создания ресурса. Откройте бесплатный урок и выполните практику.
В бесплатном курсе FastAPI вы затем соедините маршруты, базу, регистрацию и тесты в одном приложении. Когда понадобится воспроизводимо запускать этот backend на другом компьютере, откройте программу Docker для разработчика: в ней есть образы, контейнеры и Compose для приложения с базой.
Продолжить на практике
- FastAPI для начинающих: API с базой данных и тестами ↗
Соберёте свой первый полноценный backend на FastAPI: с базой данных, регистрацией, JWT-авторизацией и автотестами. 34 коротких урока и 140 заданий с проверкой — бесплатно.
