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] *}