# Пользовательские классы Request и APIRoute { #custom-request-and-apiroute-class }

В некоторых случаях может понадобиться переопределить логику, используемую классами `Request` и `APIRoute`.

В частности, это может быть хорошей альтернативой логике в middleware.

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

/// danger | Опасность

Это «продвинутая» возможность.

Если вы только начинаете работать с **FastAPI**, возможно, стоит пропустить этот раздел.

///

## Сценарии использования { #use-cases }

Некоторые сценарии:

* Преобразование тел запросов, не в формате JSON, в JSON (например, [`msgpack`](https://msgpack.org/index.html)).
* Распаковка тел запросов, сжатых с помощью gzip.
* Автоматическое логирование всех тел запросов.

## Обработка пользовательского кодирования тела запроса { #handling-custom-request-body-encodings }

Посмотрим как использовать пользовательский подкласс `Request` для распаковки gzip-запросов.

И подкласс `APIRoute`, чтобы использовать этот пользовательский класс запроса.

### Создать пользовательский класс `GzipRequest` { #create-a-custom-gziprequest-class }

/// tip | Совет

Это учебный пример, демонстрирующий принцип работы. Если вам нужна поддержка Gzip, вы можете использовать готовый [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware).

///

Сначала создадим класс `GzipRequest`, который переопределит метод `Request.body()` и распакует тело запроса при наличии соответствующего HTTP-заголовка.

Если в заголовке нет `gzip`, он не будет пытаться распаковывать тело.

Таким образом, один и тот же класс маршрута сможет обрабатывать как gzip-сжатые, так и несжатые запросы.

{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[9:16] *}

### Создать пользовательский класс `GzipRoute` { #create-a-custom-gziproute-class }

Далее создадим пользовательский подкласс `fastapi.routing.APIRoute`, который будет использовать `GzipRequest`.

На этот раз он переопределит метод `APIRoute.get_route_handler()`.

Этот метод возвращает функцию. Именно эта функция получает HTTP-запрос и возвращает HTTP-ответ.

Здесь мы используем её, чтобы создать `GzipRequest` из исходного HTTP-запроса.

{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[19:27] *}

/// note | Технические детали

У `Request` есть атрибут `request.scope` — это просто Python-`dict`, содержащий метаданные, связанные с HTTP-запросом.

У `Request` также есть `request.receive` — функция для «получения» тела запроса.

И `dict` `scope`, и функция `receive` являются частью спецификации ASGI.

Именно этих двух компонентов — `scope` и `receive` — достаточно, чтобы создать новый экземпляр `Request`.

Чтобы узнать больше о `Request`, см. [документацию Starlette о запросах](https://www.starlette.dev/requests/).

///

Единственное, что делает по-другому функция, возвращённая `GzipRequest.get_route_handler`, — преобразует `Request` в `GzipRequest`.

Благодаря этому наш `GzipRequest` позаботится о распаковке данных (при необходимости) до передачи их в наши *операции пути*.

Дальше вся логика обработки остаётся прежней.

Но благодаря изменениям в `GzipRequest.body` тело запроса будет автоматически распаковано при необходимости, когда оно будет загружено **FastAPI**.

## Доступ к телу запроса в обработчике исключений { #accessing-the-request-body-in-an-exception-handler }

/// tip | Совет

Для решения этой задачи, вероятно, намного проще использовать `body` в пользовательском обработчике `RequestValidationError` ([Обработка ошибок](../tutorial/handling-errors.md#use-the-requestvalidationerror-body)).

Но этот пример всё равно актуален и показывает, как взаимодействовать с внутренними компонентами.

///

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

Нужно лишь обработать запрос внутри блока `try`/`except`:

{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[14,16] *}

Если произойдёт исключение, экземпляр `Request` всё ещё будет в области видимости, поэтому мы сможем прочитать тело запроса и использовать его при обработке ошибки:

{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[17:19] *}

## Пользовательский класс `APIRoute` в роутере { #custom-apiroute-class-in-a-router }

Вы также можете задать параметр `route_class` у `APIRouter`:

{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[26] *}

В этом примере *операции пути*, объявленные в `router`, будут использовать пользовательский класс `TimedRoute` и получат дополнительный HTTP-заголовок `X-Response-Time` в ответе с временем, затраченным на формирование ответа:

{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[13:20] *}
