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

Тело HTTP-запроса

Этот раздел покажет, как извлекать и проверять body с помощью Rapidy.

Описание

HTTP тело запроса — это часть запроса, передающая данные от клиента к серверу. Оно играет ключевую роль в методах POST, PUT и PATCH, которые используются для создания, обновления и модификации ресурсов.

Например, при POST-запросе для создания учетной записи пользователя данные передаются в теле запроса.

from pydantic import BaseModel
from rapidy.http import post, Body

class UserData(BaseModel):
    username: str
    password: str

@post('/')
async def handler(
    user_data: UserData = Body(),
) -> ...:

Атрибуты Body

content_type

# `application/json` by default
content_type: str | ContentType = ContentType.json
Определяет ожидаемый сервером тип данных в body.

Подробнее про enum ContentType можно прочитать здесь.

Rapidy использует заданный content_type для корректного извлечения данных.

Основные поддерживаемые типы:

  • application/json
  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/* — любые MIME-типы с текстовыми данными
  • application/octet-stream

Если сервер ожидает формат, который Rapidy явно не поддерживает (например, video/mpeg), данные будут извлечены как bytes и переданы в pydantic-модель без обработки.


check_content_type

Определяет, нужно ли проверять заголовок Content-Type.

  • При True (значение по умолчанию) Rapidy сравнит переданный заголовок Content-Type с ожидаемым content_type. Если они не совпадают, клиенту вернется ошибка:
{
    "errors": [
        {
            "type": "ExtractError",
            "loc": [
                "body"
            ],
            "msg": "Failed to extract body data: Expected Content-Type `text/plain`, got `<current_request_content_type>`"
        }
    ]
}

json_decoder

Позволяет задать пользовательский json_decoder для обработки JSON-данных в теле запроса.

Работает только при content_type="application/json".

По умолчанию Rapidy использует json.loads без параметров.

Эквивалентные примеры:

from rapidy.http import post, Body

@post('/')
async def handler(
    data: str = Body(),
) -> ...:
или
import json
from rapidy.http import post, Body

@post('/')
async def handler(
    data: str = Body(json_decoder=json.loads),
) -> ...:

Если требуется кастомная обработка JSON, передайте в json_decoder любой вызываемый объект, принимающий str.

Ожидаемый тип данных: Callable[[str], Any].

Пример с пользовательским декодером:

from typing import Any
from rapidy.http import post, Body

def custom_json_decoder(data: str) -> ...:
    ...

@post('/')
async def handler(
    data: Any = Body(json_decoder=custom_json_decoder),
) -> ...:

Чтобы использовать json.loads с параметрами или передать декодер с аргументами, воспользуйтесь functools.partial:

import json
from functools import partial
from typing import Any, OrderedDict
from rapidy.http import post, Body

decoder = partial(json.loads, object_pairs_hook=OrderedDict)

@post('/')
async def handler(
    data: Any = Body(json_decoder=decoder),
) -> ...:

Извлечение без валидации

Большинство типов Body поддерживают извлечение данных без валидации.

Этот метод не рекомендуется.

Если валидация отключена, параметр вернет базовую структуру aiohttp.

Подробнее об этом можно прочитать в разделе Извлечение без валидации для каждого типа body.

Способы отключения валидации:

Явное отключение

from pydantic import BaseModel
from rapidy.http import post, Body

class BodyData(BaseModel):
    ...

@post('/')
async def handler(
    data: BodyData = Body(validate=False),
) -> ...:

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

@post('/')
async def handler(
    data: Any = Body(),
) -> ...:

Отсутствие аннотации типа

Если тип не указан, по умолчанию используется Any.

@post('/')
async def handler(
    data=Body(),
) -> ...:


Значения по умолчанию

Большинство типов Body поддерживают значения по умолчанию.

Если HTTP-запрос не содержит тела, параметр получит указанное значение по умолчанию (если оно задано).

Примеры использования

Указано значение по умолчанию

from pydantic import BaseModel
from rapidy.http import post, Body

class BodyData(BaseModel):
    ...

@post('/')
async def handler(
    data: BodyData = Body('some_data'),
    # or
    data: BodyData = Body(default_factory=lambda: 'some_data'),
) -> ...:

Опциональное тело запроса

from pydantic import BaseModel
from rapidy.http import post, Body

class BodyData(BaseModel):
    ...

@post('/')
async def handler(
    data: BodyData | None = Body(),
    # or
    data: Optional[BodyData] = Body(),
    # or
    data: Union[BodyData, None] = Body(),
) -> ...:

Подробнее об этом можно прочитать в разделе Значения по умолчанию для каждого типа body.