13 KiB
OpenAPI Callbacks
Вы можете создать API с операцией пути, которая может инициировать запрос к внешнему API, созданному кем-то другим (возможно, тем же разработчиком, который будет использовать ваше API).
Процесс, который происходит, когда ваше API-приложение вызывает внешний API, называется "обратным вызовом" (callback). Потому что программа, написанная внешним разработчиком, отправляет запрос в ваше API, а затем ваше API делает "обратный вызов", отправляя запрос во внешний API (который, вероятно, был создан тем же разработчиком).
В этом случае вам может понадобиться документировать, как этот внешний API должен выглядеть. Какие операции пути он должен иметь, какое тело ожидать, какой ответ должен возвращать и т. д.
Приложение с обратными вызовами
Давайте посмотрим на всё это на примере.
Представьте, что вы разрабатываете приложение, которое позволяет создавать счета-фактуры.
Эти счета-фактуры будут иметь id
, title
(необязательно), customer
и total
.
Пользователь вашего API (внешний разработчик) создаст счет-фактуру в вашем API с помощью POST-запроса.
Затем ваше API (давайте представим):
- Отправит счет некоторому клиенту внешнего разработчика.
- Соберет деньги.
- Отправит уведомление обратно пользователю API (внешнему разработчику).
- Это будет сделано путем отправки POST-запроса (из вашего API) на какой-то внешний API, предоставленный тем внешним разработчиком (это и есть "обратный вызов").
Обычное приложение FastAPI
Давайте сначала посмотрим, как будет выглядеть обычное API-приложение до добавления обратного вызова.
Оно будет иметь операцию пути, которая будет получать тело Invoice
, и параметр запроса callback_url
, который будет содержать URL для обратного вызова.
Эта часть довольно обычная, большая часть кода, вероятно, уже знакома вам:
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[9:13,36:53] *}
/// tip | Совет
Параметр запроса callback_url
использует тип Pydantic Url.
///
Единственное, что ново, это callbacks=invoices_callback_router.routes
в качестве аргумента для декоратора операции пути. Мы посмотрим, что это такое дальше.
Документирование обратного вызова
Фактический код обратного вызова будет сильно зависеть от вашего собственного API-приложения.
И, вероятно, будет значительно отличаться для разных приложений.
Это может быть всего одна или две строчки кода, например:
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 или Requests.
///
Написание кода документации для обратного вызова
Этот код не будет выполняться в вашем приложении, нам он нужен только для того, чтобы документировать, как этот внешний API должен выглядеть.
Но вы уже знаете, как легко создать автоматическую документацию для API с использованием FastAPI.
Итак, мы собираемся использовать эти знания, чтобы задокументировать, как внешний API должен выглядеть... создав операции пути, которые внешний API должен реализовать (те, которые ваше API будет вызывать).
/// tip | Совет
При написании кода для документирования обратного вызова может быть полезно представить, что вы — это тот внешний разработчик. И что вы в данный момент реализуете внешний API, а не ваше API.
Временное принятие этой точки зрения (внешнего разработчика) может помочь вам почувствовать, что более очевидно, где разместить параметры, модель Pydantic для тела, для ответа и т. д. для этого внешнего API.
///
Создание APIRouter
для обратного вызова
Сначала создайте новый APIRouter
, который будет содержать один или несколько обратных вызовов.
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[3,25] *}
Создание операции пути для обратного вызова
Чтобы создать операцию пути для обратного вызова, используйте тот же APIRouter
, который вы создали выше.
Это должно выглядеть так же, как обычная операция пути в FastAPI:
- Вероятно, должно быть объявлено тело, которое она должна получить, например,
body: InvoiceEvent
. - Также может быть объявлена модель ответа, которую она должна возвращать, например,
response_model=InvoiceEventReceived
.
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[16:18,21:22,28:32] *}
Существует 2 основных отличия от обычной операции пути:
- Она не должна содержать никакого реального кода, потому что ваше приложение никогда не будет вызывать этот код. Он используется только для документирования внешнего API. Поэтому функция может просто содержать
pass
. - Путь может содержать выражение OpenAPI 3 (см. ниже), где могут использоваться переменные с параметрами и частями исходного запроса, отправленного в ваше API.
Выражение пути обратного вызова
Путь обратного вызова может содержать выражение OpenAPI 3, которое может содержать части исходного запроса, отправленного в ваше API.
В данном случае это str
:
"{$callback_url}/invoices/{$request.body.id}"
Таким образом, если пользователь вашего API (внешний разработчик) отправляет запрос в ваше API на:
https://yourapi.com/invoices/?callback_url=https://www.external.org/events
с JSON-телом:
{
"id": "2expen51ve",
"customer": "Mr. Richie Rich",
"total": "9999"
}
тогда ваше API обработает счет, и в какой-то момент позже отправит запрос для обратного вызова на callback_url
(во внешний API):
https://www.external.org/events/invoices/2expen51ve
с JSON-телом, содержащим что-то вроде:
{
"description": "Payment celebration",
"paid": true
}
и оно будет ожидать ответ от этого внешнего API с JSON-телом как:
{
"ok": true
}
/// tip | Совет
Обратите внимание, как URL для обратного вызова содержит URL, полученный в виде параметра запроса в callback_url
(https://www.external.org/events
) и также id
счета-фактуры из JSON-тела (2expen51ve
).
///
Добавление маршрутизатора для обратных вызовов
На этом этапе у вас есть нужные операции пути для обратных вызовов (те, которые внешний разработчик должен реализовать во внешнем API) в созданном выше маршрутизаторе для обратных вызовов.
Теперь используйте параметр callbacks
в декораторе операции пути вашего API, чтобы передать атрибут .routes
(на самом деле это просто list
маршрутов/операций путей) из этого маршрутизатора для обратных вызовов:
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[35] *}
/// tip | Совет
Обратите внимание, что вы передаете не сам маршрутизатор (invoices_callback_router
) в callback=
, а атрибут .routes
, как в invoices_callback_router.routes
.
///
Проверка документации
Теперь вы можете запустить своё приложение и перейти на http://127.0.0.1:8000/docs.
Вы увидите вашу документацию, включая раздел "Callbacks" для вашей операции пути, который показывает, как должен выглядеть внешний API:
