Перейти к содержанию

Тело запроса

Когда вам необходимо отправить данные из клиента (например, браузера) в ваш API, вы отправляете их как тело запроса.

Тело запроса — это данные, отправляемые клиентом в ваш API. Тело ответа — это данные, которые ваш API отправляет клиенту.

Ваш API почти всегда должен отправлять тело ответа. Но клиентам не обязательно всегда отправлять тело запроса: иногда они запрашивают только путь, возможно с некоторыми параметрами запроса, но без тела.

Чтобы объявить тело запроса, используйте модели Pydantic, со всей их мощью и преимуществами.

Информация

Чтобы отправить данные, используйте один из методов: POST (чаще всего), PUT, DELETE или PATCH.

Отправка тела с запросом GET имеет неопределённое поведение в спецификациях, тем не менее это поддерживается FastAPI, но только для очень сложных/крайних случаев использования.

Поскольку это не рекомендуется, интерактивная документация со Swagger UI не будет отображать информацию для тела при использовании GET, а промежуточные прокси-серверы могут не поддерживать такой вариант запроса.

Импортируйте BaseModel из Pydantic

Первое, что нужно сделать, — импортировать BaseModel из пакета pydantic:

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item):
    return item
🤓 Other versions and variants
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item):
    return item

Создайте модель данных

Затем опишите свою модель данных как класс, наследующийся от BaseModel.

Используйте стандартные типы Python для всех атрибутов:

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item):
    return item
🤓 Other versions and variants
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item):
    return item

Так же, как при объявлении параметров запроса: когда атрибут модели имеет значение по умолчанию, он не обязателен. Иначе он обязателен. Используйте None, чтобы сделать его просто необязательным.

Например, модель выше описывает такой JSON "объект" (или Python dict):

{
    "name": "Foo",
    "description": "An optional description",
    "price": 45.2,
    "tax": 3.5
}

...так как description и tax являются необязательными (со значением по умолчанию None), такой JSON "объект" тоже будет корректным:

{
    "name": "Foo",
    "price": 45.2
}

Объявите её как параметр

Чтобы добавить её в вашу операцию пути, объявите её так же, как вы объявляли параметры пути и параметры запроса:

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item):
    return item
🤓 Other versions and variants
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item):
    return item

...и укажите тип параметра как созданную вами модель, Item.

Результаты

Всего лишь с этой аннотацией типов Python FastAPI:

  • Считает тело запроса как JSON.
  • Приведёт данные к соответствующим типам (если потребуется).
  • Проведёт валидацию данных.
    • Если данные некорректны, вернёт понятную и наглядную ошибку, указывающую, где именно и что было некорректно.
  • Передаст полученные данные в параметр item.
    • Поскольку внутри функции вы объявили его с типом Item, у вас будет поддержка со стороны редактора кода (автозавершение и т. п.) для всех атрибутов и их типов.
  • Сгенерирует определения JSON Schema для вашей модели; вы можете использовать их и в других местах, если это имеет смысл для вашего проекта.
  • Эти схемы будут частью сгенерированной схемы OpenAPI и будут использоваться автоматической документацией UIs.

Автоматическая документация

JSON Schema ваших моделей будет частью сгенерированной схемы OpenAPI и будет отображаться в интерактивной документации API:

А также они будут использоваться в документации API внутри каждой операции пути, где это требуется:

Поддержка редактора кода

В вашем редакторе кода внутри функции вы получите подсказки по типам и автозавершение повсюду (этого бы не было, если бы вы получали dict вместо модели Pydantic):

Также вы получите проверку ошибок при некорректных операциях с типами:

Это не случайность — весь фреймворк построен вокруг такого дизайна.

И это было тщательно протестировано ещё на этапе проектирования, до реализации, чтобы убедиться, что всё будет работать со всеми редакторами.

В сам Pydantic даже были внесены некоторые изменения для поддержки этого.

Предыдущие скриншоты сделаны в Visual Studio Code.

Но вы получите такую же поддержку редактора кода в PyCharm и большинстве других редакторов Python:

Совет

Если вы используете PyCharm в качестве редактора кода, вы можете использовать плагин Pydantic PyCharm Plugin.

Он улучшает поддержку моделей Pydantic в редакторе кода, включая:

  • автозавершение
  • проверки типов
  • рефакторинг
  • поиск
  • инспекции

Использование модели

Внутри функции вам доступны все атрибуты объекта модели напрямую:

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item):
    item_dict = item.dict()
    if item.tax is not None:
        price_with_tax = item.price + item.tax
        item_dict.update({"price_with_tax": price_with_tax})
    return item_dict
🤓 Other versions and variants
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item):
    item_dict = item.dict()
    if item.tax is not None:
        price_with_tax = item.price + item.tax
        item_dict.update({"price_with_tax": price_with_tax})
    return item_dict

Информация

В Pydantic v1 метод назывался .dict(), в Pydantic v2 он был помечен как устаревший (но всё ещё поддерживается) и переименован в .model_dump().

Примеры здесь используют .dict() для совместимости с Pydantic v1, но если вы можете использовать Pydantic v2, используйте .model_dump().

Тело запроса + параметры пути

Вы можете одновременно объявить параметры пути и тело запроса.

FastAPI распознает, что параметры функции, соответствующие параметрам пути, должны быть получены из пути, а параметры функции, объявленные как модели Pydantic, должны быть получены из тела запроса.

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None


app = FastAPI()


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    return {"item_id": item_id, **item.dict()}
🤓 Other versions and variants
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


app = FastAPI()


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    return {"item_id": item_id, **item.dict()}

Тело запроса + параметры пути + параметры запроса

Вы также можете одновременно объявить параметры тела, пути и запроса.

FastAPI распознает каждый из них и возьмёт данные из правильного источника.

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None


app = FastAPI()


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, q: str | None = None):
    result = {"item_id": item_id, **item.dict()}
    if q:
        result.update({"q": q})
    return result
🤓 Other versions and variants
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


app = FastAPI()


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, q: Union[str, None] = None):
    result = {"item_id": item_id, **item.dict()}
    if q:
        result.update({"q": q})
    return result

Параметры функции будут распознаны следующим образом:

  • Если параметр также объявлен в пути, он будет использоваться как параметр пути.
  • Если параметр имеет скалярный тип (например, int, float, str, bool и т. п.), он будет интерпретирован как параметр запроса.
  • Если параметр объявлен как тип модели Pydantic, он будет интерпретирован как тело запроса.

Заметка

FastAPI понимает, что значение q не является обязательным из-за значения по умолчанию = None.

Аннотации типов str | None (Python 3.10+) или Union[str, None] (Python 3.8+) не используются FastAPI для определения обязательности; он узнает, что параметр не обязателен, потому что у него есть значение по умолчанию = None.

Но добавление аннотаций типов позволит вашему редактору кода лучше вас поддерживать и обнаруживать ошибки.

Без Pydantic

Если вы не хотите использовать модели Pydantic, вы также можете использовать параметры Body. См. раздел документации Тело — Несколько параметров: Единичные значения в теле.