自訂回應 - HTML、串流、檔案、其他¶
預設情況下,FastAPI 會使用 JSONResponse
返回回應。
您可以透過直接返回 Response
來覆蓋它,如直接返回回應中所示。
但如果您直接返回 Response
(或任何子類別,例如 JSONResponse
),資料將不會自動轉換(即使您宣告了 response_model
),文件也不會自動產生(例如,在產生的 OpenAPI 中包含特定的「媒體類型」,作為 HTTP 標頭 Content-Type
的一部分)。
但您也可以在路徑操作裝飾器中使用 response_class
參數宣告您想要使用的 Response
(例如任何 Response
子類別)。
您從路徑操作函式返回的內容將會放入該 Response
中。
如果該 Response
具有 JSON 媒體類型 (application/json
),就像 JSONResponse
和 UJSONResponse
的情況一樣,您返回的資料將會使用您在路徑操作裝飾器中宣告的任何 Pydantic response_model
自動轉換(和過濾)。
注意事項
如果您使用沒有媒體類型的回應類別,FastAPI 將預期您的回應沒有內容,因此它不會在其產生的 OpenAPI 文件中記錄回應格式。
使用 ORJSONResponse
¶
例如,如果您正在追求效能,您可以安裝並使用 orjson
並將回應設定為 ORJSONResponse
。
匯入您想要使用的 Response
類別(子類別)並在路徑操作裝飾器中宣告它。
對於大型回應,直接返回 Response
比返回字典要快得多。
這是因為預設情況下,FastAPI 會檢查裡面的每個項目,並使用教學中說明的相同JSON 相容編碼器確保它可以序列化為 JSON。這讓您可以返回任意物件,例如資料庫模型。
但是,如果您確定您返回的內容可以透過 JSON 序列化,您可以將其直接傳遞給回應類別,並避免 FastAPI 在將您的返回內容傳遞給回應類別之前透過 jsonable_encoder
傳遞它所產生的額外負荷。
from fastapi import FastAPI
from fastapi.responses import ORJSONResponse
app = FastAPI()
@app.get("/items/", response_class=ORJSONResponse)
async def read_items():
return ORJSONResponse([{"item_id": "Foo"}])
資訊
參數 response_class
也將用於定義回應的「媒體類型」。
在這種情況下,HTTP 標頭 Content-Type
將設定為 application/json
。
並且它將在 OpenAPI 中如此記錄。
提示
ORJSONResponse
僅在 FastAPI 中可用,Starlette 中沒有。
HTML 回應¶
要直接從 FastAPI 返回帶有 HTML 的回應,請使用 HTMLResponse
。
- 匯入
HTMLResponse
。 - 將
HTMLResponse
作為路徑操作裝飾器的參數response_class
傳遞。
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/items/", response_class=HTMLResponse)
async def read_items():
return """
<html>
<head>
<title>Some HTML in here</title>
</head>
<body>
<h1>Look ma! HTML!</h1>
</body>
</html>
"""
資訊
參數 response_class
也將用於定義回應的「媒體類型」。
在這種情況下,HTTP 標頭 Content-Type
將設定為 text/html
。
並且它將在 OpenAPI 中如此記錄。
返回 Response
¶
如同在 直接返回響應 中所見,您也可以透過在 *路徑操作* 中直接返回響應來覆寫它。
以上述相同的例子來說,返回一個 HTMLResponse
看起來會像這樣:
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/items/")
async def read_items():
html_content = """
<html>
<head>
<title>Some HTML in here</title>
</head>
<body>
<h1>Look ma! HTML!</h1>
</body>
</html>
"""
return HTMLResponse(content=html_content, status_code=200)
警告
由您的 *路徑操作函式* 直接返回的 Response
將不會記錄在 OpenAPI 中(例如,Content-Type
不會被記錄),並且不會顯示在自動互動式文件中。
資訊
當然,實際的 Content-Type
標頭、狀態碼等將來自您返回的 Response
物件。
在 OpenAPI 中記錄並覆寫 Response
¶
如果您想要從函式內部覆寫響應,但同時在 OpenAPI 中記錄「媒體類型」,您可以使用 response_class
參數並返回一個 Response
物件。
然後,response_class
將僅用於記錄 OpenAPI *路徑操作*,但您的 Response
將按原樣使用。
直接返回一個 HTMLResponse
¶
例如,它看起來可能像這樣:
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
def generate_html_response():
html_content = """
<html>
<head>
<title>Some HTML in here</title>
</head>
<body>
<h1>Look ma! HTML!</h1>
</body>
</html>
"""
return HTMLResponse(content=html_content, status_code=200)
@app.get("/items/", response_class=HTMLResponse)
async def read_items():
return generate_html_response()
在此範例中,函式 generate_html_response()
已經生成並返回一個 Response
,而不是以 str
返回 HTML。
透過返回呼叫 generate_html_response()
的結果,您已經返回了一個 Response
,它將覆寫預設的 FastAPI 行為。
但由於您也在 response_class
中傳遞了 HTMLResponse
,FastAPI 將知道如何在 OpenAPI 和互動式文件中將其記錄為帶有 text/html
的 HTML。
可用的響應¶
以下是一些可用的響應。
請記住,您可以使用 Response
返回任何其他內容,甚至建立自定義子類別。
「技術細節」
您也可以使用 from starlette.responses import HTMLResponse
。
為了方便開發者,FastAPI 提供了與 starlette.responses
相同的 fastapi.responses
。但大多數可用的響應都直接來自 Starlette。
Response
¶
主要的 Response
類別,所有其他響應都繼承自它。
您可以直接返回它。
它接受以下參數:
content
- 一個str
或bytes
。status_code
- 一個int
HTTP 狀態碼。headers
- 一個字串的dict
。media_type
- 一個表示媒體類型的str
。例如"text/html"
。
FastAPI(實際上是 Starlette)會自動包含一個 Content-Length 標頭。它還會根據 media_type
包含一個 Content-Type 標頭,並為文字類型附加一個字元集。
from fastapi import FastAPI, Response
app = FastAPI()
@app.get("/legacy/")
def get_legacy_data():
data = """<?xml version="1.0"?>
<shampoo>
<Header>
Apply shampoo here.
</Header>
<Body>
You'll have to use soap here.
</Body>
</shampoo>
"""
return Response(content=data, media_type="application/xml")
HTMLResponse
¶
接受一些文字或位元組並返回一個 HTML 響應,如您上面所讀。
PlainTextResponse
¶
接受一些文字或位元組並返回一個純文字響應。
from fastapi import FastAPI
from fastapi.responses import PlainTextResponse
app = FastAPI()
@app.get("/", response_class=PlainTextResponse)
async def main():
return "Hello World"
JSONResponse
¶
接受一些資料並返回一個 application/json
編碼的響應。
這是 FastAPI 中使用的預設響應,如您上面所讀。
ORJSONResponse
¶
一個使用 orjson
的快速替代 JSON 響應,如您上面所讀。
資訊
這需要安裝 orjson
,例如使用 pip install orjson
。
UJSONResponse
¶
使用 ujson
的另一種 JSON 回應方式。
資訊
這需要安裝 ujson
,例如使用 pip install ujson
。
警告
在處理一些邊緣情況時,ujson
比 Python 內建的實現方式較不謹慎。
from fastapi import FastAPI
from fastapi.responses import UJSONResponse
app = FastAPI()
@app.get("/items/", response_class=UJSONResponse)
async def read_items():
return [{"item_id": "Foo"}]
提示
ORJSONResponse
可能是更快的替代方案。
RedirectResponse
¶
返回 HTTP 重定向。預設使用 307 狀態碼(暫時重定向)。
您可以直接返回 RedirectResponse
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/typer")
async def redirect_typer():
return RedirectResponse("https://typer.dev.org.tw")
或者您可以在 response_class
參數中使用它
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/fastapi", response_class=RedirectResponse)
async def redirect_fastapi():
return "https://fastapi.dev.org.tw"
如果您這樣做,則可以直接從您的*路徑操作*函數返回 URL。
在這種情況下,使用的 status_code
將是 RedirectResponse
的預設值,即 307
。
您也可以將 status_code
參數與 response_class
參數一起使用
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/pydantic", response_class=RedirectResponse, status_code=302)
async def redirect_pydantic():
return "https://docs.pydantic.dev/"
StreamingResponse
¶
接收一個異步產生器或一般的產生器/迭代器,並串流傳輸回應主體。
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
app = FastAPI()
async def fake_video_streamer():
for i in range(10):
yield b"some fake video bytes"
@app.get("/")
async def main():
return StreamingResponse(fake_video_streamer())
將 StreamingResponse
與類檔案物件一起使用¶
如果您有一個類檔案物件(例如 open()
返回的物件),您可以建立一個產生器函數來迭代該類檔案物件。
這樣,您不必先將其全部讀入記憶體,就可以將該產生器函數傳遞給 StreamingResponse
並返回它。
這包含許多與雲端儲存、影片處理等互動的函式庫。
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
some_file_path = "large-video-file.mp4"
app = FastAPI()
@app.get("/")
def main():
def iterfile(): # (1)
with open(some_file_path, mode="rb") as file_like: # (2)
yield from file_like # (3)
return StreamingResponse(iterfile(), media_type="video/mp4")
- 這是產生器函數。它是一個「產生器函數」,因為它內部包含
yield
陳述式。 - 透過使用
with
區塊,我們確保在產生器函數完成後關閉類檔案物件。因此,在它完成發送回應之後。 -
這個
yield from
告訴函數迭代名為file_like
的東西。然後,對於迭代的每個部分,將該部分作為來自此產生器函數 (iterfile
) 的值產生。因此,它是一個將「產生」工作轉移到內部其他物件的產生器函數。
透過這種方式,我們可以將其放入
with
區塊中,從而確保在完成後關閉類檔案物件。
提示
請注意,由於我們在此處使用不支援 async
和 await
的標準 open()
,因此我們使用普通的 def
宣告路徑操作。
FileResponse
¶
以非同步方式串流傳輸檔案作為回應。
使用與其他回應類型不同的參數集來實例化
path
- 要串流傳輸的檔案的路徑。headers
- 要包含的任何自訂標頭,以字典形式提供。media_type
- 提供媒體類型的字串。如果未設定,將使用檔名或路徑來推斷媒體類型。filename
- 如果設定,這將包含在回應的Content-Disposition
中。
檔案回應將包含適當的 Content-Length
、Last-Modified
和 ETag
標頭。
from fastapi import FastAPI
from fastapi.responses import FileResponse
some_file_path = "large-video-file.mp4"
app = FastAPI()
@app.get("/")
async def main():
return FileResponse(some_file_path)
您也可以使用 response_class
參數
from fastapi import FastAPI
from fastapi.responses import FileResponse
some_file_path = "large-video-file.mp4"
app = FastAPI()
@app.get("/", response_class=FileResponse)
async def main():
return some_file_path
在這種情況下,您可以直接從您的*路徑操作*函數返回檔案路徑。
自訂回應類別¶
您可以建立自己的自訂回應類別,繼承自 Response
並使用它。
例如,假設您想使用 orjson
,但使用一些未包含在 ORJSONResponse
類別中的自訂設定。
假設您希望它返回縮排和格式化的 JSON,因此您想使用 orjson 選項 orjson.OPT_INDENT_2
。
您可以建立一個 CustomORJSONResponse
。您主要要做的是建立一個 Response.render(content)
方法,該方法以 bytes
形式返回內容。
from typing import Any
import orjson
from fastapi import FastAPI, Response
app = FastAPI()
class CustomORJSONResponse(Response):
media_type = "application/json"
def render(self, content: Any) -> bytes:
assert orjson is not None, "orjson must be installed"
return orjson.dumps(content, option=orjson.OPT_INDENT_2)
@app.get("/", response_class=CustomORJSONResponse)
async def main():
return {"message": "Hello World"}
現在,它不會回傳
{"message": "Hello World"}
...而是會回傳
{
"message": "Hello World"
}
當然,除了格式化 JSON 之外,您可能會找到更好的方法來利用它。 😉
預設回應類別¶
建立 FastAPI 類別實例或 APIRouter
時,您可以指定預設要使用的回應類別。
定義此參數的是 default_response_class
。
在下面的範例中,FastAPI 將在所有*路徑操作*中預設使用 ORJSONResponse
,而不是 JSONResponse
。
from fastapi import FastAPI
from fastapi.responses import ORJSONResponse
app = FastAPI(default_response_class=ORJSONResponse)
@app.get("/items/")
async def read_items():
return [{"item_id": "Foo"}]
提示
您仍然可以像以前一樣在*路徑操作*中覆寫 response_class
。
其他文件¶
您也可以使用 responses
在 OpenAPI 中宣告媒體類型和許多其他細節:OpenAPI 中的其他回應。