# Користувацькі класи Request та APIRoute { #custom-request-and-apiroute-class }

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

Зокрема, це може бути доброю альтернативою логіці в проміжному програмному забезпеченні.

Наприклад, якщо потрібно прочитати або змінити тіло запиту до того, як його обробить ваш застосунок.

/// 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()`, щоб розпаковувати тіло за наявності відповідного заголовка.

Якщо в заголовку немає `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()`.

Цей метод повертає функцію. І саме ця функція прийме запит і поверне відповідь.

Тут ми використовуємо її, щоб створити `GzipRequest` з початкового запиту.

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

/// note | Технічні деталі

У `Request` є атрибут `request.scope` - це просто Python `dict`, що містить метадані, пов'язані із запитом.

Також `Request` має `request.receive` - це функція для «отримання» тіла запиту.

`scope` `dict` і функція `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` і матимуть додатковий заголовок відповіді `X-Response-Time` із часом, витраченим на формування відповіді:

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