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

Тело запроса

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

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

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

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

Информация

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

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

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

Импортирование BaseModel из Pydantic

Первое, что вам необходимо сделать, это импортировать BaseModel из пакета pydantic:

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 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):

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

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

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

Объявление как параметра функции

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

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 и используются для автоматического документирования UI.

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

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

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

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

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

Также вы будете получать ошибки в случае несоответствия типов:

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

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

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

На всех предыдущих скриншотах используется Visual Studio Code.

Но у вас будет такая же поддержка и с PyCharm, и вообще с любым редактором Python:

Подсказка

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

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

  • автодополнения,
  • проверки типов,
  • рефакторинга,
  • поиска,
  • инспектирования.

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

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

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:
        price_with_tax = item.price + item.tax
        item_dict.update({"price_with_tax": price_with_tax})
    return item_dict

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

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

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

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 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.

Аннотация Optional в Optional[str] не используется FastAPI, но помогает вашему редактору лучше понимать ваш код и обнаруживать ошибки.

Без Pydantic

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