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

273 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 自訂回應——HTML、串流、檔案與其他 { #custom-response-html-stream-file-others }
預設情況下,**FastAPI** 會回傳 JSON 回應。
你可以像在[直接回傳 Response](response-directly.md)中所示,直接回傳一個 `Response` 來覆寫它。
但如果你直接回傳一個 `Response`(或其子類別,例如 `JSONResponse`),資料將不會被自動轉換(即使你宣告了 `response_model`),而且文件也不會自動產生(例如,在產生的 OpenAPI 中包含 HTTP 標頭 `Content-Type` 的特定「media type」)。
你也可以在「路徑操作裝飾器」中使用 `response_class` 參數,宣告要使用的 `Response`(例如任意 `Response` 子類別)。
你從「路徑操作函式」回傳的內容,會被放進該 `Response` 中。
/// note
若你使用的回應類別沒有 media type,FastAPI 會假設你的回應沒有內容,因此不會在產生的 OpenAPI 文件中記錄回應格式。
///
## JSON 回應 { #json-responses }
FastAPI 預設回傳 JSON 回應。
如果你宣告了[回應模型](../tutorial/response-model.md)FastAPI 會使用 Pydantic 將資料序列化為 JSON。
如果你沒有宣告回應模型,FastAPI 會使用在[JSON 相容編碼器](../tutorial/encoder.md)中解釋的 `jsonable_encoder`,並將結果放進 `JSONResponse`
如果你宣告的 `response_class` 具有 JSON 的 media type`application/json`),像 `JSONResponse`,你回傳的資料會自動以你在「路徑操作裝飾器」中宣告的任何 Pydantic `response_model` 進行轉換(與過濾)。但資料不會由 Pydantic 直接序列化成 JSON 位元組;取而代之,會先經由 `jsonable_encoder` 轉換,然後交給 `JSONResponse` 類別,該類別會使用 Python 標準的 JSON 函式庫將其序列化為位元組。
### JSON 效能 { #json-performance }
簡而言之,若你想要最佳效能,請使用[回應模型](../tutorial/response-model.md),並且不要在「路徑操作裝飾器」中宣告 `response_class`
{* ../../docs_src/response_model/tutorial001_01_py310.py ln[15:17] hl[16] *}
## HTML 回應 { #html-response }
要直接從 **FastAPI** 回傳 HTML,使用 `HTMLResponse`
- 匯入 `HTMLResponse`
- 在「路徑操作裝飾器」中,將 `HTMLResponse` 傳給 `response_class` 參數。
{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *}
/// note
參數 `response_class` 也會用來定義回應的「media type」。
在此情況下,HTTP 標頭 `Content-Type` 會被設為 `text/html`
而且它會以此形式被記錄到 OpenAPI 中。
///
### 回傳 `Response` { #return-a-response }
如[直接回傳 Response](response-directly.md)所示,你也可以在「路徑操作」中直接回傳以覆寫回應。
上面的相同範例,回傳 `HTMLResponse`,可以像這樣:
{* ../../docs_src/custom_response/tutorial003_py310.py hl[2,7,19] *}
/// warning
由你的「路徑操作函式」直接回傳的 `Response` 不會被記錄進 OpenAPI(例如不會記錄 `Content-Type`),也不會出現在自動產生的互動式文件中。
///
/// note
當然,實際的 `Content-Type` 標頭、狀態碼等,會來自你回傳的 `Response` 物件。
///
### 在 OpenAPI 中文件化並覆寫 `Response` { #document-in-openapi-and-override-response }
如果你想在函式內覆寫回應,同時又要在 OpenAPI 中記錄「media type」,你可以同時使用 `response_class` 參數並回傳一個 `Response` 物件。
此時,`response_class` 只會用於記錄該 OpenAPI「路徑操作」,而你回傳的 `Response` 將會如實使用。
#### 直接回傳 `HTMLResponse` { #return-an-htmlresponse-directly }
例如,可能會像這樣:
{* ../../docs_src/custom_response/tutorial004_py310.py hl[7,21,23] *}
在這個例子中,函式 `generate_html_response()` 已經產生並回傳了一個 `Response`,而不是把 HTML 當作 `str` 回傳。
透過回傳 `generate_html_response()` 的結果,你其實已經回傳了一個 `Response`,這會覆寫 **FastAPI** 的預設行為。
但因為你同時也在 `response_class` 中傳入了 `HTMLResponse`**FastAPI** 便能在 OpenAPI 與互動式文件中,將其以 `text/html` 的 HTML 形式記錄:
<img src="/img/tutorial/custom-response/image01.png">
## 可用的回應 { #available-responses }
以下是一些可用的回應類別。
記得你可以用 `Response` 回傳其他任何東西,甚至建立自訂的子類別。
/// note | 技術細節
你也可以使用 `from starlette.responses import HTMLResponse`
**FastAPI**`starlette.responses``fastapi.responses` 提供給你(開發者)做為方便之用。但大多數可用的回應其實直接來自 Starlette。
///
### `Response` { #response }
主要的 `Response` 類別,其他回應皆繼承自它。
你也可以直接回傳它。
它接受以下參數:
- `content` - `str``bytes`
- `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` { #htmlresponse }
接收文字或位元組並回傳 HTML 回應,如上所述。
### `PlainTextResponse` { #plaintextresponse }
接收文字或位元組並回傳純文字回應。
{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *}
### `JSONResponse` { #jsonresponse }
接收資料並回傳 `application/json` 編碼的回應。
這是 **FastAPI** 的預設回應,如上所述。
/// note | 技術細節
但如果你宣告了回應模型或回傳型別,將會直接用它來把資料序列化為 JSON,並直接回傳具有正確 JSON media type 的回應,而不會使用 `JSONResponse` 類別。
這是取得最佳效能的理想方式。
///
### `RedirectResponse` { #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_code``response_class` 參數:
{* ../../docs_src/custom_response/tutorial006c_py310.py hl[2,7,9] *}
### `StreamingResponse` { #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`,你大概會想遵循[資料串流](./stream-data.md)中的作法,這樣更方便,並且會在底層幫你處理取消。
如果你要串流 JSON Lines,請參考教學:[串流 JSON Lines](../tutorial/stream-json-lines.md)。
///
### `FileResponse` { #fileresponse }
以非同步串流方式將檔案作為回應。
它在初始化時所需的參數與其他回應型別不同:
- `path` - 要串流的檔案路徑。
- `headers` - 要包含的自訂標頭,字典形式。
- `media_type` - 描述 media type 的字串。若未設定,將根據檔名或路徑推斷 media type。
- `filename` - 若設定,會包含在回應的 `Content-Disposition` 中。
檔案回應會包含適當的 `Content-Length``Last-Modified``ETag` 標頭。
{* ../../docs_src/custom_response/tutorial009_py310.py hl[2,10] *}
你也可以使用 `response_class` 參數:
{* ../../docs_src/custom_response/tutorial009b_py310.py hl[2,8,10] *}
在此情況下,你可以在「路徑操作函式」中直接回傳檔案路徑。
## 自訂回應類別 { #custom-response-class }
你可以建立自己的自訂回應類別,繼承自 `Response` 並加以使用。
例如,假設你要使用 [`orjson`](https://github.com/ijl/orjson) 並套用一些設定。
假設你想回傳縮排且格式化的 JSON,因此要使用 orjson 選項 `orjson.OPT_INDENT_2`
你可以建立 `CustomORJSONResponse`。你主要需要做的是建立一個 `Response.render(content)` 方法,將內容以 `bytes` 回傳:
{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *}
現在,不再是回傳:
```json
{"message": "Hello World"}
```
...這個回應會回傳:
```json
{
"message": "Hello World"
}
```
當然,你大概能找到比格式化 JSON 更好的方式來利用這個能力。😉
### `orjson` 或回應模型 { #orjson-or-response-model }
如果你追求效能,使用[回應模型](../tutorial/response-model.md) 大概會比使用 `orjson` 回應更好。
有了回應模型,FastAPI 會使用 Pydantic 直接將資料序列化為 JSON,而不需要像其他情況那樣先經過 `jsonable_encoder` 之類的中介步驟。
而且在底層,Pydantic 用來序列化為 JSON 的 Rust 機制和 `orjson` 相同,因此用回應模型已經能獲得最佳效能。
## 預設回應類別 { #default-response-class }
在建立 **FastAPI** 類別實例或 `APIRouter` 時,你可以指定預設要使用哪個回應類別。
用來設定的是 `default_response_class` 參數。
在下面的例子中,**FastAPI** 會在所有「路徑操作」中預設使用 `HTMLResponse`,而不是 JSON。
{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}
/// tip
你仍然可以在「路徑操作」中像以前一樣覆寫 `response_class`
///
## 其他文件化選項 { #additional-documentation }
你也可以在 OpenAPI 中使用 `responses` 宣告 media type 與其他許多細節:[在 OpenAPI 中的額外回應](additional-responses.md)。