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

HTTP-заголовки

В этом разделе рассмотрим, как извлекать и проверять заголовки с помощью Rapidy.

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

Описание

HTTP-заголовки позволяют клиенту и серверу обмениваться дополнительной информацией в HTTP-запросах и ответах.

Извлечение одного заголовка

Header позволяет получить конкретный заголовок.

from rapidy.http import get, Header

@get('/')
async def handler(
    host: str = Header(alias='Host'),
) -> ...:
from rapidy.http import get, Header

@get('/')
async def handler(
    host: str = Header(alias='Host'),
    keep_alive: str = Header(alias='Keep-Alive'),
) -> ...:

Извлечение всех заголовков

Headers позволяет извлечь сразу все заголовки.

Извлечение в заранее заданную схему

pydantic.BaseModel

from pydantic import BaseModel, Field
from rapidy.http import get, Headers

class HeadersData(BaseModel):
    host: str = Field(alias='Host')
    keep_alive: str = Field(alias='Keep-Alive')

@get('/')
async def handler(
    headers_data: HeadersData = Headers(),
) -> ...:

dataclasses.dataclass

dataclasses.dataclass поддерживаются в качестве модели, но задать alias стандартными средствами dataclasses невозможно.

from dataclasses import dataclass
from rapidy.http import get, Headers

@dataclass
class HeadersData:
    host: str
    keep_alive: str  # cannot extract if header name is 'Keep-Alive'

@get('/')
async def handler(
    headers_data: HeadersData = Headers(),
) -> ...:
# {"errors": [{"type": "missing", "loc": ["header", "keep_alive" ], "msg": "Field required"}]}

Извлечение в словарь

from rapidy.http import get, Headers

@get('/')
async def handler(
    headers_data: dict[str, str] = Headers(),
) -> ...:
# {Host': '0.0.0.0:8080', 'Connection': 'keep-alive', 'Upgrade-Insecure-Requests': '1', 'User-Agent': '...'}

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

Отключение валидации не рекомендуется.

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

  • Headerstr
  • HeadersCIMultiDictProxy[str]

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

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

from multidict import CIMultiDictProxy
from rapidy.http import get, Header, Headers

@get('/')
async def handler_1(
    header_host: str = Header(alias='Host', validate=False)
) -> ...:
    # "0.0.0.0:8080"

@get('/')
async def handler_2(
    headers_data: CIMultiDictProxy[str] = Headers(validate=False)
) -> ...:
    # <CIMultiDictProxy('Host': '0.0.0.0:8080', 'Connection': 'keep-alive', 'Upgrade-Insecure-Requests': '1', 'User-Agent': '...')>

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

from typing import Any

@get('/')
async def handler_1(
    header_host: Any = Header(alias='Host')
) -> ...:
    # "0.0.0.0:8080"

@get('/')
async def handler_2(
    headers_data: Any = Headers()
) -> ...:
    # <CIMultiDictProxy('Host': '0.0.0.0:8080', 'Connection': 'keep-alive', 'Upgrade-Insecure-Requests': '1', 'User-Agent': '...')>

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

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

from typing import Any

@get('/')
async def handler_1(
    header_host=Header(alias='Host')
) -> ...:
    # "0.0.0.0:8080"

@get('/')
async def handler_2(
    headers_data=Headers()
) -> ...:
    # <CIMultiDictProxy('Host': '0.0.0.0:8080', 'Connection': 'keep-alive', 'Upgrade-Insecure-Requests': '1', 'User-Agent': '...')>

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

Значение по умолчанию для Header будет использовано, если в запросе отсутствует заголовок с указанным именем.

Значение по умолчанию для Headers будет использовано, если в запросе нет ни одного заголовка.

Значение по умолчанию для Headers в реальности не применяется.

Любой HTTP-клиент всегда отправляет базовые заголовки, поэтому этот случай практически невозможен. Однако, если такое вдруг произойдёт, механизм отработает корректно.

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

@get('/')
async def handler(
    some_header: str = Header(alias='Some-Header', default='SomeValue'),
) -> ...:
from typing import Annotated

@get('/')
async def handler(
    some_header: Annotated[str, Header(alias='Some-Header', default='SomeValue')],
) -> ...:
from typing import Annotated

@get('/')
async def handler(
    some_header: Annotated[str, Header(alias='Some-Header')] = 'SomeValue',
) -> ...:

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

@get('/')
async def handler(
    some_header: str = Header(alias='Some-Header', default_factory=lambda: 'SomeValue'),
) -> ...:
from typing import Annotated

@get('/')
async def handler(
    some_header: Annotated[str, Header(alias='Some-Header', default_factory=lambda:'SomeValue')],
) -> ...:

Нельзя одновременно использовать default и default_factory.

При попытке задать оба параметра будет вызвано исключение pydantic:

TypeError('cannot specify both default and default_factory')

Предупреждения и особенности

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

Невозможно использовать Header и Headers одновременно в одном обработчике.

@get('/')
async def handler(
    host: str = Header(alias='Host'),
    headers_data: HeadersData = Headers(),
) -> ...:

При запуске приложения будет вызвано исключение AnotherDataExtractionTypeAlreadyExistsError.

------------------------------
Attribute with this data extraction type cannot be added to the handler - another data extraction type is already use in handler.

Handler path: `main.py`
Handler name: `handler`
Attribute name: `headers_data`
------------------------------

Атрибут alias в Headers

Атрибут alias не работает в параметре Headers().

@get('/')
async def handler(
    headers_data: HeadersData = Headers(alias='SomeName'),  # <-- alias not working
) -> ...:

Как извлекаются сырые данные

В Rapidy используется метод headers объекта Request, после чего полученные данные передаются в pydantic для валидации.

Как происходит извлечение внутри Rapidy

async def extract_headers(request: Request) -> CIMultiDictProxy[str]:
    return request.headers

Rapidy использует встроенные механизмы aiohttp.

Подробнее об объекте aiohttp.Request и методах извлечения данных можно узнать здесь.