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

7.8 KiB
Raw Blame History

OpenAPI 回呼

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

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

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

帶有回呼的應用

我們用一個例子來看。

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

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

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

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

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

一般的 FastAPI 應用

先看看在加入回呼之前,一個一般的 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。我們接下來會看到那是什麼。

文件化回呼

實際的回呼程式碼會高度依賴你的 API 應用本身。

而且很可能每個應用都差很多。

它可能就只有一兩行,例如:

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

///

撰寫回呼的文件化程式碼

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

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

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

/// tip

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

暫時採用這個外部開發者的視角有助於讓你更直覺地決定該把參數、body 的 Pydantic 模型、response 的模型等放在哪裡,對於那個「外部 API」會更清楚。

///

建立一個回呼用的 APIRouter

先建立一個新的 APIRouter,用來放一個或多個回呼。

{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[1,23] *}

建立回呼的「路徑操作」

要建立回呼的「路徑操作」,就使用你上面建立的同一個 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」的請求中的部分欄位。

回呼路徑表達式

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

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

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

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

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

並附上這個 JSON body

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

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

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

其 JSON body 大致包含:

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

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

{
    "ok": true
}

/// tip

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

///

加入回呼 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

///

檢查文件

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

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