You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

6.8 KiB

Користувацькі класи Request та APIRoute

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

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

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

/// danger | Обережно

Це «просунута» можливість.

Якщо ви тільки починаєте працювати з FastAPI, можливо, варто пропустити цей розділ.

///

Випадки використання

Деякі варіанти використання:

  • Перетворення не-JSON тіл запитів на JSON (наприклад, msgpack).
  • Розпакування тіл запитів, стиснених gzip.
  • Автоматичне логування всіх тіл запитів.

Обробка користувацьких кодувань тіла запиту

Розгляньмо, як використати користувацький підклас Request для розпакування gzip-запитів.

А також підклас APIRoute, щоб застосувати цей користувацький клас запиту.

Створіть користувацький клас GzipRequest

/// tip | Порада

Це навчальний приклад, щоб продемонструвати принцип роботи. Якщо вам потрібна підтримка Gzip, скористайтеся вбудованим GzipMiddleware.

///

Спочатку створимо клас GzipRequest, який перевизначить метод Request.body(), щоб розпаковувати тіло за наявності відповідного заголовка.

Якщо в заголовку немає gzip, він не намагатиметься розпаковувати тіло.

Таким чином один і той самий клас маршруту зможе обробляти як стиснені gzip, так і нестиснені запити.

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

Створіть користувацький клас GzipRoute

Далі створимо користувацький підклас 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 про запити.

///

Єдине, що робить інакше функція, повернена GzipRequest.get_route_handler, - перетворює Request на GzipRequest.

Завдяки цьому наш GzipRequest подбає про розпакування даних (за потреби) перед передаванням їх у наші операції шляху.

Після цього вся логіка обробки залишається тією самою.

А завдяки змінам у GzipRequest.body тіло запиту за потреби буде автоматично розпаковане під час завантаження FastAPI.

Доступ до тіла запиту в обробнику виключень

/// tip | Порада

Щоб розв’язати це саме завдання, скоріш за все, простіше використати body у користувацькому обробнику RequestValidationError (Обробка помилок).

Але цей приклад усе ще корисний і показує, як взаємодіяти з внутрішніми компонентами.

///

Ми також можемо скористатися цим підходом, щоб отримати доступ до тіла запиту в обробнику виключень.

Усе, що потрібно, - обробити запит усередині блоку 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 у маршрутизаторі

Можна також встановити параметр 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] *}