187 lines
12 KiB
Markdown
187 lines
12 KiB
Markdown
# Зворотні виклики OpenAPI { #openapi-callbacks }
|
||
|
||
Ви можете створити API з *операцією шляху*, яка ініціюватиме запит до *зовнішнього API*, створеного кимось іншим (ймовірно тим самим розробником, який буде *використовувати* ваш API).
|
||
|
||
Процес, що відбувається, коли ваш застосунок API викликає *зовнішній API*, називається «зворотний виклик». Тому що програмне забезпечення, написане зовнішнім розробником, надсилає запит до вашого API, а потім ваш API *виконує зворотний виклик*, надсилаючи запит до *зовнішнього API* (його, ймовірно, також створив той самий розробник).
|
||
|
||
У такому випадку вам може знадобитися задокументувати, яким має бути той *зовнішній API*: яку *операцію шляху* він має мати, яке тіло очікувати, яку відповідь повертати тощо.
|
||
|
||
## Застосунок зі зворотними викликами { #an-app-with-callbacks }
|
||
|
||
Розгляньмо це на прикладі.
|
||
|
||
Уявімо, що ви розробляєте застосунок для створення рахунків.
|
||
|
||
Ці рахунки матимуть `id`, `title` (необов'язково), `customer` і `total`.
|
||
|
||
Користувач вашого API (зовнішній розробник) створить рахунок у вашому API за допомогою POST-запиту.
|
||
|
||
Потім ваш API буде (уявімо):
|
||
|
||
- Надсилати рахунок деякому клієнту зовнішнього розробника.
|
||
- Отримувати оплату.
|
||
- Надсилати сповіщення назад користувачу API (зовнішньому розробнику).
|
||
- Це буде зроблено шляхом надсилання POST-запиту (з *вашого API*) до деякого *зовнішнього API*, наданого тим зовнішнім розробником (це і є «зворотний виклик»).
|
||
|
||
## Звичайний застосунок **FastAPI** { #the-normal-fastapi-app }
|
||
|
||
Спочатку подивімося, як виглядав би звичайний застосунок API до додавання зворотного виклику.
|
||
|
||
Він матиме *операцію шляху*, яка отримуватиме тіло `Invoice`, і параметр запиту `callback_url`, що міститиме URL для зворотного виклику.
|
||
|
||
Ця частина цілком звична, більшість коду вам, ймовірно, уже знайома:
|
||
|
||
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[7:11,34:51] *}
|
||
|
||
/// tip | Порада
|
||
|
||
Параметр запиту `callback_url` використовує тип Pydantic [Url](https://docs.pydantic.dev/latest/api/networks/).
|
||
|
||
///
|
||
|
||
Єдина нова річ - це `callbacks=invoices_callback_router.routes` як аргумент *декоратора операції шляху*. Далі розглянемо, що це таке.
|
||
|
||
## Документування зворотного виклику { #documenting-the-callback }
|
||
|
||
Фактичний код зворотного виклику сильно залежатиме від вашого застосунку API.
|
||
|
||
І, ймовірно, сильно відрізнятиметься від застосунку до застосунку.
|
||
|
||
Це можуть бути лише один-два рядки коду, наприклад:
|
||
|
||
```Python
|
||
callback_url = "https://example.com/api/v1/invoices/events/"
|
||
httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
|
||
```
|
||
|
||
Але, можливо, найважливіша частина зворотного виклику - переконатися, що користувач вашого API (зовнішній розробник) правильно реалізує *зовнішній API* відповідно до даних, які *ваш API* надсилатиме в тілі запиту зворотного виклику тощо.
|
||
|
||
Тому далі ми додамо код, щоб задокументувати, яким має бути цей *зовнішній API*, щоб приймати зворотний виклик від *вашого API*.
|
||
|
||
Ця документація з'явиться в Swagger UI за адресою `/docs` у вашому API і дасть змогу зовнішнім розробникам зрозуміти, як створити *зовнішній API*.
|
||
|
||
У цьому прикладі сам зворотний виклик не реалізовано (це може бути лише один рядок коду), лише частину з документацією.
|
||
|
||
/// tip | Порада
|
||
|
||
Фактичний зворотний виклик - це просто HTTP-запит.
|
||
|
||
Реалізуючи зворотний виклик самостійно, ви можете скористатися, наприклад, [HTTPX](https://www.python-httpx.org) або [Requests](https://requests.readthedocs.io/).
|
||
|
||
///
|
||
|
||
## Напишіть код документації для зворотного виклику { #write-the-callback-documentation-code }
|
||
|
||
Цей код не виконуватиметься у вашому застосунку, він потрібен лише, щоб *задокументувати*, яким має бути *зовнішній API*.
|
||
|
||
Але ви вже знаєте, як легко створювати автоматичну документацію для API за допомогою **FastAPI**.
|
||
|
||
Тож ми скористаємося цими знаннями, щоб задокументувати, яким має бути *зовнішній API*... створивши *операції шляху*, які має реалізувати зовнішній API (ті, які викликатиме ваш API).
|
||
|
||
/// tip | Порада
|
||
|
||
Пишучи код для документування зворотного виклику, корисно уявити, що ви - той *зовнішній розробник*. І що ви зараз реалізуєте *зовнішній API*, а не *ваш API*.
|
||
|
||
Тимчасово прийнявши цю точку зору (*зовнішнього розробника*), вам буде очевидніше, куди помістити параметри, яку Pydantic-модель використати для тіла, для відповіді тощо для того *зовнішнього API*.
|
||
|
||
///
|
||
|
||
### Створіть `APIRouter` зворотного виклику { #create-a-callback-apirouter }
|
||
|
||
Спочатку створіть новий `APIRouter`, який міститиме один або кілька зворотних викликів.
|
||
|
||
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[1,23] *}
|
||
|
||
### Створіть *операцію шляху* зворотного виклику { #create-the-callback-path-operation }
|
||
|
||
Щоб створити *операцію шляху* зворотного виклику, використайте той самий `APIRouter`, який ви створили вище.
|
||
|
||
Вона має виглядати як звичайна *операція шляху* FastAPI:
|
||
|
||
- Ймовірно має містити оголошення тіла, яке вона приймає, наприклад `body: InvoiceEvent`.
|
||
- І також може містити оголошення відповіді, яку вона повертає, наприклад `response_model=InvoiceEventReceived`.
|
||
|
||
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[14:16,19:20,26:30] *}
|
||
|
||
Є 2 основні відмінності від звичайної *операції шляху*:
|
||
|
||
- Їй не потрібен реальний код, адже ваш застосунок ніколи не викликатиме цей код. Вона використовується лише для документування *зовнішнього API*. Тому функція може просто містити `pass`.
|
||
- *Шлях* може містити [вираз OpenAPI 3](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression) (див. нижче), де можна використовувати змінні з параметрами та частини оригінального запиту, надісланого до *вашого API*.
|
||
|
||
### Вираз шляху зворотного виклику { #the-callback-path-expression }
|
||
|
||
*Шлях* зворотного виклику може містити [вираз OpenAPI 3](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression), який включає частини оригінального запиту, надісланого до *вашого API*.
|
||
|
||
У цьому випадку це `str`:
|
||
|
||
```Python
|
||
"{$callback_url}/invoices/{$request.body.id}"
|
||
```
|
||
|
||
Отже, якщо користувач вашого API (зовнішній розробник) надішле запит до *вашого API* на:
|
||
|
||
```
|
||
https://yourapi.com/invoices/?callback_url=https://www.external.org/events
|
||
```
|
||
|
||
з JSON-тілом:
|
||
|
||
```JSON
|
||
{
|
||
"id": "2expen51ve",
|
||
"customer": "Mr. Richie Rich",
|
||
"total": "9999"
|
||
}
|
||
```
|
||
|
||
тоді *ваш API* опрацює рахунок і згодом надішле запит зворотного виклику на `callback_url` (*зовнішній API*):
|
||
|
||
```
|
||
https://www.external.org/events/invoices/2expen51ve
|
||
```
|
||
|
||
з JSON-тілом на кшталт:
|
||
|
||
```JSON
|
||
{
|
||
"description": "Payment celebration",
|
||
"paid": true
|
||
}
|
||
```
|
||
|
||
і очікуватиме відповідь від того *зовнішнього API* з JSON-тілом на кшталт:
|
||
|
||
```JSON
|
||
{
|
||
"ok": true
|
||
}
|
||
```
|
||
|
||
/// tip | Порада
|
||
|
||
Зверніть увагу, що використаний URL зворотного виклику містить URL, отриманий як параметр запиту в `callback_url` (`https://www.external.org/events`), а також `id` рахунку зсередини JSON-тіла (`2expen51ve`).
|
||
|
||
///
|
||
|
||
### Додайте маршрутизатор зворотного виклику { #add-the-callback-router }
|
||
|
||
На цьому етапі ви маєте потрібні *операції шляху зворотного виклику* (ті, які має реалізувати *зовнішній розробник* у *зовнішньому API*) у створеному вище маршрутизаторі зворотного виклику.
|
||
|
||
Тепер використайте параметр `callbacks` у *декораторі операції шляху вашого API*, щоб передати атрибут `.routes` з цього маршрутизатора зворотного виклику:
|
||
|
||
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[33] *}
|
||
|
||
/// tip | Порада
|
||
|
||
Зверніть увагу, що ви передаєте не сам маршрутизатор (`invoices_callback_router`) у `callbacks=`, а його `.routes`, тобто `invoices_callback_router.routes`. FastAPI використає ці маршрути, щоб згенерувати документацію OpenAPI для зворотних викликів.
|
||
|
||
///
|
||
|
||
### Перевірте документацію { #check-the-docs }
|
||
|
||
Тепер ви можете запустити застосунок і перейти за адресою [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
|
||
|
||
Ви побачите вашу документацію з розділом «Callbacks» для вашої *операції шляху*, який показує, як має виглядати *зовнішній API*:
|
||
|
||
<img src="/img/tutorial/openapi-callbacks/image01.png">
|