Back to Fastapi

OpenAPI 回呼 { #openapi-callbacks }

docs/zh-hant/docs/advanced/openapi-callbacks.md

0.137.17.5 KB
Original Source

OpenAPI 回呼 { #openapi-callbacks }

你可以建立一個含有「路徑操作(path operation)」的 API,該操作會觸發對某個「外部 API(external API)」的請求(通常由使用你 API 的同一位開發者提供)。

當你的 API 應用呼叫「外部 API」時發生的過程稱為「回呼(callback)」。因為外部開發者撰寫的軟體會先向你的 API 發出請求,接著你的 API 再「回呼」,也就是向(可能同一位開發者建立的)外部 API 發送請求。

在這種情況下,你可能想要文件化說明該外部 API 應該長什麼樣子。它應該有哪些「路徑操作」、應該接受什麼 body、應該回傳什麼 response,等等。

帶有回呼的應用 { #an-app-with-callbacks }

我們用一個例子來看。

想像你開發了一個允許建立發票的應用。

這些發票會有 idtitle(可選)、customertotal

你的 API 的使用者(外部開發者)會透過一個 POST 請求在你的 API 中建立一張發票。

然後你的 API 會(讓我們想像):

  • 將發票寄給該外部開發者的某位客戶。
  • 代收款項。
  • 再把通知回傳給 API 使用者(外部開發者)。
    • 這會透過從「你的 API」向該外部開發者提供的「外部 API」送出 POST 請求完成(這就是「回呼」)。

一般的 FastAPI 應用 { #the-normal-fastapi-app }

先看看在加入回呼之前,一個一般的 API 應用會長什麼樣子。

它會有一個接收 Invoice body 的「路徑操作」,以及一個查詢參數 callback_url,其中包含用於回呼的 URL。

這部分很正常,多數程式碼你應該已經很熟悉了:

{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[7:11,34:51] *}

/// tip

callback_url 查詢參數使用的是 Pydantic 的 Url 型別。

///

唯一新的地方是在「路徑操作裝飾器」中加入參數 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」在回呼請求 body 中要送出的資料格式,等等。

因此,接下來我們要加上用來「文件化」說明,該「外部 API」應該長什麼樣子,才能接收來自「你的 API」的回呼。

這份文件會出現在你的 API 的 Swagger UI /docs,讓外部開發者知道該如何建置「外部 API」。

這個範例不會實作回呼本身(那可能就只是一行程式碼),只會實作文件的部分。

/// tip

實際的回呼就是一個 HTTP 請求。

當你自己實作回呼時,可以使用像是 HTTPXRequests

///

撰寫回呼的文件化程式碼 { #write-the-callback-documentation-code }

這段程式碼在你的應用中不會被執行,我們只需要它來「文件化」說明那個「外部 API」應該長什麼樣子。

不過,你已經知道如何用 FastAPI 輕鬆為 API 建立自動文件。

所以我們會用同樣的方式,來文件化「外部 API」應該長什麼樣子... 也就是建立外部 API 應該實作的「路徑操作(們)」(那些「你的 API」會去呼叫的操作)。

/// tip

在撰寫回呼的文件化程式碼時,把自己想像成那位「外部開發者」會很有幫助。而且你現在是在實作「外部 API」,不是「你的 API」。

暫時採用這個(外部開發者)的視角,有助於讓你更直覺地決定該把參數、body 的 Pydantic 模型、response 的模型等放在哪裡,對於那個「外部 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,例如 body: InvoiceEvent
  • 也可以宣告它應該回傳的 response,例如 response_model=InvoiceEventReceived

{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[14:16,19:20,26:30] *}

和一般「路徑操作」相比有兩個主要差異:

  • 不需要任何實際程式碼,因為你的應用永遠不會呼叫這段程式。它只用來文件化「外部 API」。因此函式可以只有 pass
  • 「路徑」可以包含一個 OpenAPI 3 表達式(見下文),可使用參數與原始送到「你的 API」的請求中的部分欄位。

回呼路徑表達式 { #the-callback-path-expression }

回呼的「路徑」可以包含一個 OpenAPI 3 表達式,能引用原本送到「你的 API」的請求中的部分內容。

在這個例子中,它是一個 str

Python
"{$callback_url}/invoices/{$request.body.id}"

所以,如果你的 API 使用者(外部開發者)向「你的 API」送出這樣的請求:

https://yourapi.com/invoices/?callback_url=https://www.external.org/events

並附上這個 JSON body:

JSON
{
    "id": "2expen51ve",
    "customer": "Mr. Richie Rich",
    "total": "9999"
}

那麼「你的 API」會處理這張發票,並在稍後某個時點,向 callback_url(也就是「外部 API」)送出回呼請求:

https://www.external.org/events/invoices/2expen51ve

其 JSON body 大致包含:

JSON
{
    "description": "Payment celebration",
    "paid": true
}

而它會預期該「外部 API」回傳的 JSON body 例如:

JSON
{
    "ok": true
}

/// tip

注意回呼所用的 URL,包含了在查詢參數 callback_url 中收到的 URL(https://www.external.org/events),以及來自 JSON body 內的發票 id2expen51ve)。

///

加入回呼 router { #add-the-callback-router }

此時你已經在先前建立的回呼 router 中,擁有所需的回呼「路徑操作(們)」(也就是「外部開發者」應該在「外部 API」中實作的那些)。

現在在「你的 API 的路徑操作裝飾器」中使用參數 callbacks,將該回呼 router 的屬性 .routes(實際上就是一個由路由/「路徑操作」所組成的 list)傳入:

{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[33] *}

/// tip

注意你傳給 callback= 的不是整個 router 本身(invoices_callback_router),而是它的屬性 .routes,也就是 invoices_callback_router.routes

///

檢查文件 { #check-the-docs }

現在你可以啟動應用,並前往 http://127.0.0.1:8000/docs

你會在文件中看到你的「路徑操作」包含一個「Callbacks」區塊,顯示「外部 API」應該長什麼樣子: