Урок курса
POST, JSON-тело и первая модель запроса
FastAPI для начинающих: API с базой данных и тестамиДо сих пор все маршруты в уроках были GET: браузер или /docs запрашивали данные, а сервер отвечал списком или одним объектом. Теперь нужно принять данные от клиента — JSON в теле запроса. Для этого подключается другой HTTP-метод и первая модель Pydantic.
POST-маршрут и несовпадение HTTP-метода
FastAPI сопоставляет входящий запрос сразу по двум параметрам: пути и методу. Декоратор @app.get("/api/rooms") регистрирует обработчик только для метода GET. Если клиент отправит на тот же путь POST-запрос, FastAPI вернёт 405 Method Not Allowed — маршрут найден, но метод не совпадает.
Чтобы принимать POST на /api/rooms, нужен отдельный декоратор:
@app.post("/api/rooms")
def create_room():
return {"ok": True}
Этот и GET-маршрут могут сосуществовать на одном пути — каждый обслуживает свой метод своей функцией. FastAPI просто выбирает нужный обработчик по методу входящего запроса.
В интерфейсе /docs POST и GET отображаются отдельными секциями для одного пути. Ошибка 405 появляется именно тогда, когда путь зарегистрирован, но для другого метода: не стоит путать её с 404, которая означает, что пути вообще нет.
Этот обработчик возвращает одинаковый {"ok": true} независимо от отправленного тела: аргумента для его получения пока нет.
Обязательные поля модели BaseModel
Чтобы FastAPI умел читать JSON-тело и проверять его структуру, нужна модель — класс, наследующий BaseModel из Pydantic:
from pydantic import BaseModel
class RoomIn(BaseModel):
name: str
floor: int
capacity: int
Каждое поле — просто аннотированный атрибут класса. Никаких __init__, __repr__ или ручной валидации писать не нужно: всё это берёт на себя Pydantic.
Все три поля здесь обязательны: у них нет значения по умолчанию. Если входящий JSON не содержит хотя бы одно из них или передаёт значение несовместимого типа (например, строку "шесть" вместо числа в capacity), Pydantic не сможет построить объект RoomIn. FastAPI при этом вернёт 422 Unprocessable Entity и не вызовет функцию-обработчик вообще.
Pydantic также делает мягкое приведение типов там, где это безопасно: строка "2" превратится в int 2 для поля floor. Но строку "два" привести к int не получится — это уже 422.
Важно понимать, что RoomIn описывает контракт входных данных. Всё, что не прошло проверку, до вашего кода не доходит.
Аргумент-тело в обработчике: получение объекта RoomIn и доступ к полям
Когда аргумент функции-обработчика аннотирован подклассом BaseModel, FastAPI автоматически читает его из тела запроса — не из пути и не из query-строки. Механизм вывода источника встроен в FastAPI: он смотрит на тип аннотации.
Полный рабочий файл выглядит так:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class RoomIn(BaseModel):
name: str
floor: int
capacity: int
@app.post("/api/rooms")
def create_room(room: RoomIn):
return {"received": room.name, "floor": room.floor, "capacity": room.capacity}
Внутри create_room переменная room — это уже готовый объект RoomIn с проверенными полями. Обращение к ним — обычный доступ к атрибутам: room.name, room.floor, room.capacity.
Чтобы проверить в /docs: откройте секцию POST /api/rooms, нажмите Try it out, в поле Request body введите:
{"name": "Лондон", "floor": 2, "capacity": 6}
Нажмите Execute. Ответ будет 200 и тело:
{"received": "Лондон", "floor": 2, "capacity": 6}
/docs отправляет запрос с заголовком Content-Type: application/json — он сообщает серверу формат тела. FastAPI ориентируется именно на него при разборе.
Обработчик может одновременно принимать аргумент-тело и query-параметры или path-параметры: они не конфликтуют, FastAPI разбирает каждый источник отдельно. Данные здесь не сохраняются — обработчик просто возвращает то, что получил, и это достаточно, чтобы убедиться: контракт работает.

Попробуйте решить
Маршрут объявлен как @app.post("/api/rooms") с функцией create_room(room: RoomIn), где RoomIn имеет поля name: str и capacity: int. Клиент отправляет POST /api/rooms с телом {"name": "Лондон"} — поле capacity отсутствует. Какой статус вернёт FastAPI и выполнится ли тело функции create_room?
Продолжить с проверкой и прогрессом
Откройте интерактивный раннер с заданиями урока.
