fastapi/docs/zh-hant/docs/advanced/custom-response.md

10 KiB
Raw Blame History

自訂回應——HTML、串流、檔案與其他

預設情況下,FastAPI 會回傳 JSON 回應。

你可以像在直接回傳 Response中所示,直接回傳一個 Response 來覆寫它。

但如果你直接回傳一個 Response(或其子類別,例如 JSONResponse),資料將不會被自動轉換(即使你宣告了 response_model),而且文件也不會自動產生(例如,在產生的 OpenAPI 中包含 HTTP 標頭 Content-Type 的特定「media type」

你也可以在「路徑操作裝飾器」中使用 response_class 參數,宣告要使用的 Response(例如任意 Response 子類別)。

你從「路徑操作函式」回傳的內容,會被放進該 Response 中。

/// note

若你使用的回應類別沒有 media typeFastAPI 會假設你的回應沒有內容,因此不會在產生的 OpenAPI 文件中記錄回應格式。

///

JSON 回應

FastAPI 預設回傳 JSON 回應。

如果你宣告了回應模型FastAPI 會使用 Pydantic 將資料序列化為 JSON。

如果你沒有宣告回應模型FastAPI 會使用在JSON 相容編碼器中解釋的 jsonable_encoder,並將結果放進 JSONResponse

如果你宣告的 response_class 具有 JSON 的 media typeapplication/json),像 JSONResponse,你回傳的資料會自動以你在「路徑操作裝飾器」中宣告的任何 Pydantic response_model 進行轉換(與過濾)。但資料不會由 Pydantic 直接序列化成 JSON 位元組;取而代之,會先經由 jsonable_encoder 轉換,然後交給 JSONResponse 類別,該類別會使用 Python 標準的 JSON 函式庫將其序列化為位元組。

JSON 效能

簡而言之,若你想要最佳效能,請使用回應模型,並且不要在「路徑操作裝飾器」中宣告 response_class

{* ../../docs_src/response_model/tutorial001_01_py310.py ln[15:17] hl[16] *}

HTML 回應

要直接從 FastAPI 回傳 HTML使用 HTMLResponse

  • 匯入 HTMLResponse
  • 在「路徑操作裝飾器」中,將 HTMLResponse 傳給 response_class 參數。

{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *}

/// info

參數 response_class 也會用來定義回應的「media type」。

在此情況下HTTP 標頭 Content-Type 會被設為 text/html

而且它會以此形式被記錄到 OpenAPI 中。

///

回傳 Response

直接回傳 Response所示,你也可以在「路徑操作」中直接回傳以覆寫回應。

上面的相同範例,回傳 HTMLResponse,可以像這樣:

{* ../../docs_src/custom_response/tutorial003_py310.py hl[2,7,19] *}

/// warning

由你的「路徑操作函式」直接回傳的 Response 不會被記錄進 OpenAPI例如不會記錄 Content-Type),也不會出現在自動產生的互動式文件中。

///

/// info

當然,實際的 Content-Type 標頭、狀態碼等,會來自你回傳的 Response 物件。

///

在 OpenAPI 中文件化並覆寫 Response

如果你想在函式內覆寫回應,同時又要在 OpenAPI 中記錄「media type」你可以同時使用 response_class 參數並回傳一個 Response 物件。

此時,response_class 只會用於記錄該 OpenAPI「路徑操作」而你回傳的 Response 將會如實使用。

直接回傳 HTMLResponse

例如,可能會像這樣:

{* ../../docs_src/custom_response/tutorial004_py310.py hl[7,21,23] *}

在這個例子中,函式 generate_html_response() 已經產生並回傳了一個 Response,而不是把 HTML 當作 str 回傳。

透過回傳 generate_html_response() 的結果,你其實已經回傳了一個 Response,這會覆寫 FastAPI 的預設行為。

但因為你同時也在 response_class 中傳入了 HTMLResponseFastAPI 便能在 OpenAPI 與互動式文件中,將其以 text/html 的 HTML 形式記錄:

可用的回應

以下是一些可用的回應類別。

記得你可以用 Response 回傳其他任何東西,甚至建立自訂的子類別。

/// note | 技術細節

你也可以使用 from starlette.responses import HTMLResponse

FastAPIstarlette.responsesfastapi.responses 提供給你(開發者)做為方便之用。但大多數可用的回應其實直接來自 Starlette。

///

Response

主要的 Response 類別,其他回應皆繼承自它。

你也可以直接回傳它。

它接受以下參數:

  • content - strbytes
  • status_code - int 類型的 HTTP 狀態碼。
  • headers - 由字串組成的 dict
  • media_type - 描述 media type 的 str。例如 "text/html"

FastAPI實際上是 Starlette會自動包含 Content-Length 標頭。也會根據 media_type(並為文字型別附加 charset包含 Content-Type 標頭。

{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}

HTMLResponse

接收文字或位元組並回傳 HTML 回應,如上所述。

PlainTextResponse

接收文字或位元組並回傳純文字回應。

{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *}

JSONResponse

接收資料並回傳 application/json 編碼的回應。

這是 FastAPI 的預設回應,如上所述。

/// note | 技術細節

但如果你宣告了回應模型或回傳型別,將會直接用它來把資料序列化為 JSON並直接回傳具有正確 JSON media type 的回應,而不會使用 JSONResponse 類別。

這是取得最佳效能的理想方式。

///

RedirectResponse

回傳一個 HTTP 重新導向。預設使用 307 狀態碼Temporary Redirect

你可以直接回傳 RedirectResponse

{* ../../docs_src/custom_response/tutorial006_py310.py hl[2,9] *}


或者你可以在 response_class 參數中使用它:

{* ../../docs_src/custom_response/tutorial006b_py310.py hl[2,7,9] *}

若這麼做,你就可以在「路徑操作函式」中直接回傳 URL。

在此情況下,所使用的 status_code 會是 RedirectResponse 的預設值 307


你也可以同時搭配 status_coderesponse_class 參數:

{* ../../docs_src/custom_response/tutorial006c_py310.py hl[2,7,9] *}

StreamingResponse

接收一個 async 產生器或一般的產生器/疊代器(帶有 yield 的函式),並以串流方式傳送回應本文。

{* ../../docs_src/custom_response/tutorial007_py310.py hl[3,16] *}

/// note | 技術細節

一個 async 任務只能在抵達某個 await 時才能被取消。如果沒有 await,該產生器(帶有 yield 的函式)將無法被正確取消,甚至在請求取消後仍可能持續執行。

因為這個小範例不需要任何 await 陳述式,我們加入 await anyio.sleep(0),讓事件迴圈有機會處理取消。

對於大型或無限的串流來說,這點更為重要。

///

/// tip

與其直接回傳 StreamingResponse,你大概會想遵循資料串流中的作法,這樣更方便,並且會在底層幫你處理取消。

如果你要串流 JSON Lines請參考教學串流 JSON Lines

///

FileResponse

以非同步串流方式將檔案作為回應。

它在初始化時所需的參數與其他回應型別不同:

  • path - 要串流的檔案路徑。
  • headers - 要包含的自訂標頭,字典形式。
  • media_type - 描述 media type 的字串。若未設定,將根據檔名或路徑推斷 media type。
  • filename - 若設定,會包含在回應的 Content-Disposition 中。

檔案回應會包含適當的 Content-LengthLast-ModifiedETag 標頭。

{* ../../docs_src/custom_response/tutorial009_py310.py hl[2,10] *}

你也可以使用 response_class 參數:

{* ../../docs_src/custom_response/tutorial009b_py310.py hl[2,8,10] *}

在此情況下,你可以在「路徑操作函式」中直接回傳檔案路徑。

自訂回應類別

你可以建立自己的自訂回應類別,繼承自 Response 並加以使用。

例如,假設你要使用 orjson 並套用一些設定。

假設你想回傳縮排且格式化的 JSON因此要使用 orjson 選項 orjson.OPT_INDENT_2

你可以建立 CustomORJSONResponse。你主要需要做的是建立一個 Response.render(content) 方法,將內容以 bytes 回傳:

{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *}

現在,不再是回傳:

{"message": "Hello World"}

...這個回應會回傳:

{
  "message": "Hello World"
}

當然,你大概能找到比格式化 JSON 更好的方式來利用這個能力。😉

orjson 或回應模型

如果你追求效能,使用回應模型 大概會比使用 orjson 回應更好。

有了回應模型FastAPI 會使用 Pydantic 直接將資料序列化為 JSON而不需要像其他情況那樣先經過 jsonable_encoder 之類的中介步驟。

而且在底層Pydantic 用來序列化為 JSON 的 Rust 機制和 orjson 相同,因此用回應模型已經能獲得最佳效能。

預設回應類別

在建立 FastAPI 類別實例或 APIRouter 時,你可以指定預設要使用哪個回應類別。

用來設定的是 default_response_class 參數。

在下面的例子中,FastAPI 會在所有「路徑操作」中預設使用 HTMLResponse,而不是 JSON。

{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}

/// tip

你仍然可以在「路徑操作」中像以前一樣覆寫 response_class

///

其他文件化選項

你也可以在 OpenAPI 中使用 responses 宣告 media type 與其他許多細節:在 OpenAPI 中的額外回應