# Пользовательские классы Request и APIRoute { #custom-request-and-apiroute-class } В некоторых случаях может понадобиться переопределить логику, используемую классами `Request` и `APIRoute`. В частности, это может быть хорошей альтернативой логике в middleware. Например, если вы хотите прочитать или изменить тело запроса до того, как оно будет обработано вашим приложением. /// danger | Опасность Это «продвинутая» возможность. Если вы только начинаете работать с **FastAPI**, возможно, стоит пропустить этот раздел. /// ## Сценарии использования { #use-cases } Некоторые сценарии: * Преобразование тел запросов, не в формате JSON, в JSON (например, `msgpack`). * Распаковка тел запросов, сжатых с помощью gzip. * Автоматическое логирование всех тел запросов. ## Обработка пользовательского кодирования тела запроса { #handling-custom-request-body-encodings } Посмотрим как использовать пользовательский подкласс `Request` для распаковки gzip-запросов. И подкласс `APIRoute`, чтобы использовать этот пользовательский класс запроса. ### Создать пользовательский класс `GzipRequest` { #create-a-custom-gziprequest-class } /// tip | Совет Это учебный пример, демонстрирующий принцип работы. Если вам нужна поддержка Gzip, вы можете использовать готовый [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware){.internal-link target=_blank}. /// Сначала создадим класс `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 о запросах. /// Единственное, что делает по-другому функция, возвращённая `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){.internal-link target=_blank}). Но этот пример всё равно актуален и показывает, как взаимодействовать с внутренними компонентами. /// Тем же подходом можно воспользоваться, чтобы получить доступ к телу запроса в обработчике исключений. Нужно лишь обработать запрос внутри блока `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] *}