Перейти до змісту

Зворотні виклики OpenAPI

🌐 Переклад ШІ та людьми

Цей переклад виконано ШІ під керівництвом людей. 🤝

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

Ви можете покращити цей переклад, допомігши нам краще спрямовувати AI LLM.

Англійська версія

Ви можете створити API з операцією шляху, яка ініціюватиме запит до зовнішнього API, створеного кимось іншим (ймовірно тим самим розробником, який буде використовувати ваш API).

Процес, що відбувається, коли ваш застосунок API викликає зовнішній API, називається «зворотний виклик». Тому що програмне забезпечення, написане зовнішнім розробником, надсилає запит до вашого API, а потім ваш API виконує зворотний виклик, надсилаючи запит до зовнішнього API (його, ймовірно, також створив той самий розробник).

У такому випадку вам може знадобитися задокументувати, яким має бути той зовнішній API: які операції шляху він має мати, яке тіло очікувати, яку відповідь повертати тощо.

Застосунок зі зворотними викликами

Розгляньмо це на прикладі.

Уявімо, що ви розробляєте застосунок для створення рахунків.

Ці рахунки матимуть id, title (необов'язково), customer і total.

Користувач вашого API (зовнішній розробник) створить рахунок у вашому API за допомогою POST-запиту.

Потім ваш API буде (уявімо):

  • Надсилати рахунок деякому клієнту зовнішнього розробника.
  • Отримувати оплату.
  • Надсилати сповіщення назад користувачу API (зовнішньому розробнику).
  • Це буде зроблено шляхом надсилання POST-запиту (з вашого API) до деякого зовнішнього API, наданого тим зовнішнім розробником (це і є «зворотний виклик»).

Звичайний застосунок FastAPI

Спочатку подивімося, як виглядав би звичайний застосунок API до додавання зворотного виклику.

Він матиме операцію шляху, яка отримуватиме тіло Invoice, і параметр запиту callback_url, що міститиме URL для зворотного виклику.

Ця частина цілком звична, більшість коду вам, ймовірно, уже знайома:

from fastapi import APIRouter, FastAPI
from pydantic import BaseModel, HttpUrl

app = FastAPI()


class Invoice(BaseModel):
    id: str
    title: str | None = None
    customer: str
    total: float


class InvoiceEvent(BaseModel):
    description: str
    paid: bool


class InvoiceEventReceived(BaseModel):
    ok: bool


invoices_callback_router = APIRouter()


@invoices_callback_router.post(
    "{$callback_url}/invoices/{$request.body.id}", response_model=InvoiceEventReceived
)
def invoice_notification(body: InvoiceEvent):
    pass


@app.post("/invoices/", callbacks=invoices_callback_router.routes)
def create_invoice(invoice: Invoice, callback_url: HttpUrl | None = None):
    """
    Create an invoice.

    This will (let's imagine) let the API user (some external developer) create an
    invoice.

    And this path operation will:

    * Send the invoice to the client.
    * Collect the money from the client.
    * Send a notification back to the API user (the external developer), as a callback.
        * At this point is that the API will somehow send a POST request to the
            external API with the notification of the invoice event
            (e.g. "payment successful").
    """
    # Send the invoice, collect the money, send the notification (the callback)
    return {"msg": "Invoice received"}

Порада

Параметр запиту 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.

У цьому прикладі сам зворотний виклик не реалізовано (це може бути лише один рядок коду), лише частину з документацією.

Порада

Фактичний зворотний виклик - це просто HTTP-запит.

Реалізуючи зворотний виклик самостійно, ви можете скористатися, наприклад, HTTPX або Requests.

Напишіть код документації для зворотного виклику

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

Але ви вже знаєте, як легко створювати автоматичну документацію для API за допомогою FastAPI.

Тож ми скористаємося цими знаннями, щоб задокументувати, яким має бути зовнішній API... створивши операції шляху, які має реалізувати зовнішній API (ті, які викликатиме ваш API).

Порада

Пишучи код для документування зворотного виклику, корисно уявити, що ви - той зовнішній розробник. І що ви зараз реалізуєте зовнішній API, а не ваш API.

Тимчасово прийнявши цю точку зору ( зовнішнього розробника ), вам буде очевидніше, куди помістити параметри, яку Pydantic-модель використати для тіла, для відповіді тощо для того зовнішнього API.

Створіть callback APIRouter

Спочатку створіть новий APIRouter, який міститиме один або кілька зворотних викликів.

from fastapi import APIRouter, FastAPI
from pydantic import BaseModel, HttpUrl

app = FastAPI()


class Invoice(BaseModel):
    id: str
    title: str | None = None
    customer: str
    total: float


class InvoiceEvent(BaseModel):
    description: str
    paid: bool


class InvoiceEventReceived(BaseModel):
    ok: bool


invoices_callback_router = APIRouter()


@invoices_callback_router.post(
    "{$callback_url}/invoices/{$request.body.id}", response_model=InvoiceEventReceived
)
def invoice_notification(body: InvoiceEvent):
    pass


@app.post("/invoices/", callbacks=invoices_callback_router.routes)
def create_invoice(invoice: Invoice, callback_url: HttpUrl | None = None):
    """
    Create an invoice.

    This will (let's imagine) let the API user (some external developer) create an
    invoice.

    And this path operation will:

    * Send the invoice to the client.
    * Collect the money from the client.
    * Send a notification back to the API user (the external developer), as a callback.
        * At this point is that the API will somehow send a POST request to the
            external API with the notification of the invoice event
            (e.g. "payment successful").
    """
    # Send the invoice, collect the money, send the notification (the callback)
    return {"msg": "Invoice received"}

Створіть операцію шляху зворотного виклику

Щоб створити операцію шляху зворотного виклику, використайте той самий APIRouter, який ви створили вище.

Вона має виглядати як звичайна операція шляху FastAPI:

  • Ймовірно має містити оголошення тіла, яке вона приймає, напр. body: InvoiceEvent.
  • І також може містити оголошення відповіді, яку вона повертає, напр. response_model=InvoiceEventReceived.
from fastapi import APIRouter, FastAPI
from pydantic import BaseModel, HttpUrl

app = FastAPI()


class Invoice(BaseModel):
    id: str
    title: str | None = None
    customer: str
    total: float


class InvoiceEvent(BaseModel):
    description: str
    paid: bool


class InvoiceEventReceived(BaseModel):
    ok: bool


invoices_callback_router = APIRouter()


@invoices_callback_router.post(
    "{$callback_url}/invoices/{$request.body.id}", response_model=InvoiceEventReceived
)
def invoice_notification(body: InvoiceEvent):
    pass


@app.post("/invoices/", callbacks=invoices_callback_router.routes)
def create_invoice(invoice: Invoice, callback_url: HttpUrl | None = None):
    """
    Create an invoice.

    This will (let's imagine) let the API user (some external developer) create an
    invoice.

    And this path operation will:

    * Send the invoice to the client.
    * Collect the money from the client.
    * Send a notification back to the API user (the external developer), as a callback.
        * At this point is that the API will somehow send a POST request to the
            external API with the notification of the invoice event
            (e.g. "payment successful").
    """
    # Send the invoice, collect the money, send the notification (the callback)
    return {"msg": "Invoice received"}

Є 2 основні відмінності від звичайної операції шляху:

  • Їй не потрібен реальний код, адже ваш застосунок ніколи не викликатиме цей код. Вона використовується лише для документування зовнішнього API. Тому функція може просто містити pass.
  • Шлях може містити вираз OpenAPI 3 (див. нижче), де можна використовувати змінні з параметрами та частини оригінального запиту, надісланого до вашого API.

Вираз шляху зворотного виклику

Шлях зворотного виклику може містити вираз OpenAPI 3, який включає частини оригінального запиту, надісланого до вашого API.

У цьому випадку це строка:

"{$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
}

Порада

Зверніть увагу, що використаний URL зворотного виклику містить URL, отриманий як параметр запиту в callback_url (https://www.external.org/events), а також id рахунку зсередини JSON-тіла (2expen51ve).

Додайте маршрутизатор зворотного виклику

На цьому етапі ви маєте потрібні операції шляху зворотного виклику (ті, які має реалізувати зовнішній розробник у зовнішньому API) у створеному вище маршрутизаторі зворотного виклику.

Тепер використайте параметр callbacks у декораторі операції шляху вашого API, щоб передати атрибут .routes (це насправді просто list маршрутів/операцій шляху) з цього маршрутизатора зворотного виклику:

from fastapi import APIRouter, FastAPI
from pydantic import BaseModel, HttpUrl

app = FastAPI()


class Invoice(BaseModel):
    id: str
    title: str | None = None
    customer: str
    total: float


class InvoiceEvent(BaseModel):
    description: str
    paid: bool


class InvoiceEventReceived(BaseModel):
    ok: bool


invoices_callback_router = APIRouter()


@invoices_callback_router.post(
    "{$callback_url}/invoices/{$request.body.id}", response_model=InvoiceEventReceived
)
def invoice_notification(body: InvoiceEvent):
    pass


@app.post("/invoices/", callbacks=invoices_callback_router.routes)
def create_invoice(invoice: Invoice, callback_url: HttpUrl | None = None):
    """
    Create an invoice.

    This will (let's imagine) let the API user (some external developer) create an
    invoice.

    And this path operation will:

    * Send the invoice to the client.
    * Collect the money from the client.
    * Send a notification back to the API user (the external developer), as a callback.
        * At this point is that the API will somehow send a POST request to the
            external API with the notification of the invoice event
            (e.g. "payment successful").
    """
    # Send the invoice, collect the money, send the notification (the callback)
    return {"msg": "Invoice received"}

Порада

Зверніть увагу, що ви передаєте не сам маршрутизатор (invoices_callback_router) у callback=, а атрибут .routes, тобто invoices_callback_router.routes.

Перевірте документацію

Тепер ви можете запустити застосунок і перейти за адресою http://127.0.0.1:8000/docs.

Ви побачите вашу документацію з розділом «Callbacks» для вашої операції шляху, який показує, як має виглядати зовнішній API: