# 自訂回應——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 形式記錄:
## 可用的回應 { #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)。