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

Middlewares

Описание

Middleware — это промежуточное ПО, позволяющее выполнять определённые действия до и после обработки запроса маршрутом.

from rapidy import Rapidy
from rapidy.http import middleware, get, Request, StreamResponse
from rapidy.typedefs import CallNext

@middleware
async def hello_rapidy_middleware(request: Request, call_next: CallNext) -> StreamResponse:
    print('before')
    handler_response = await call_next(request)
    print('after')
    return handler_response

@get('/')
async def handler() -> dict[str, str]:
    return {'hello': 'rapidy'}

rapidy = Rapidy(
    http_route_handlers=[handler],
    middlewares=[hello_rapidy_middleware],
)

Порядок атрибутов

В веб-обработчике первым аргументом всегда передаётся Request, а вторым — CallNext (обработчик запроса или middleware в цепочке). (Они имеют одинаковую сигнатуру — rapidy.typedefs.CallNext.)

Тип ответа обработчика

Ответ веб-обработчика или следующего middleware в цепочке всегда будет StreamResponse. Учитывайте это при проектировании ваших middleware.


Способы создания

Создавать middleware можно двумя способами.

Без параметров

Этот вариант подходит, если вам не нужно управлять запросом внутри middleware.

@middleware
async def hello_middleware(request: Request, call_next: CallNext) -> StreamResponse:

С параметрами

Используйте этот способ, если хотите более гибко управлять ответом на запрос.

@middleware(
    response_validate=...,
    response_type = ...,
    response_content_type = ...,
    response_charset = ...,
    response_zlib_executor = ...,
    response_zlib_executor_size = ...,
    response_include_fields = ...,
    response_exclude_fields = ...,
    response_by_alias = ...,
    response_exclude_unset = ...,
    response_exclude_defaults = ...,
    response_exclude_none = ...,
    response_custom_encoder = ...,
    response_json_encoder = ...,
)
async def hello_middleware(request: Request, call_next: CallNext) -> StreamResponse:

Атрибуты

Rapidy-middleware поддерживают все механизмы валидации данных, доступные в веб-обработчиках, а также работу с ответами.

Валидация

Как и веб-обработчики, middleware могут получать доступ к объектам запроса через атрибуты.

Перед тем как продолжить, рекомендуем ознакомиться с разделом Request — Управление HTTP-запросом, поскольку middleware используют такую же логику обработки параметров запроса.

Обработка Bearer-токена.

from rapidy.http import middleware, StreamResponse, Header, Request
from rapidy.typedefs import CallNext

TOKEN_REGEXP = '^[Bb]earer (?P<token>[A-Za-z0-9-_=.]*)'

@middleware
async def get_bearer_middleware(
    request: Request,
    call_next: CallNext,
    bearer_token: str = Header(alias='Authorization', pattern=TOKEN_REGEXP),
) -> StreamResponse:
    # process token here ...
    return await call_next(request)

Если вы извлекаете body как в middleware, так и в обработчике, ошибки о повторном чтении данных не возникнет.

Данные кешируются в памяти и повторно используются при валидации.


Управление ответом

Как и веб-обработчики, middleware могут управлять ответами через собственные атрибуты.

Перед тем как продолжить, рекомендуем ознакомиться с разделом Response — Управление HTTP-ответом, поскольку middleware используют ту же логику обработки ответов.

Управление ответом возможно только в случае, если middleware возвращает необработанный тип данных (любой, кроме Response или StreamResponse).

Middleware управляет ответом с помощью атрибутов.
from rapidy import Rapidy
from rapidy.http import middleware, StreamResponse, get, Request
from rapidy.enums import ContentType
from rapidy.typedefs import CallNext

@middleware(response_content_type=ContentType.text_html)
async def hello_rapidy_middleware(request: Request, call_next: CallNext) -> StreamResponse | str:
    try:
        return await call_next(request)
    except Exception:
        return 'server error'  # Content-Type='text/html'

@get('/')
async def handler() -> dict[str, str]:
    raise Exception

rapidy = Rapidy(middlewares=[hello_rapidy_middleware], http_route_handlers=[handler])
Middleware не может управлять ответом с помощью атрибутов.
from rapidy import Rapidy
from rapidy.http import middleware, StreamResponse, get, Request, Response
from rapidy.enums import ContentType
from rapidy.typedefs import CallNext

@middleware(response_content_type=ContentType.text_html)
async def hello_rapidy_middleware(request: Request, call_next: CallNext) -> StreamResponse:
    try:
        return await call_next(request)
    except Exception:
        return Response(status=500)  # Content-Type='application/octet-stream'

@get('/')
async def handler() -> dict[str, str]:
    raise Exception

rapidy = Rapidy(middlewares=[hello_rapidy_middleware], http_route_handlers=[handler])

Доступ к Response.

@middleware
async def hello_rapidy_middleware(
    request: Request,
    call_next: CallNext,
    response: Response,
) -> StreamResponse:

Response создаётся только для текущего middleware.

Middleware возвращает другой тип данных

Если middleware возвращает любой тип данных, кроме StreamResponse, укажите его в Union, чтобы Rapidy использовала этот тип при проверке ответов.

@middleware
async def my_middleware(
    request: Request,
    call_next: CallNext,
) -> int | str | StreamResponse:  # or Union[int, str, StreamResponse]
    if ...:
        return 1
    elif ...:
        return 'string'
    return await call_next(request)