本文 AI 產出,尚未審核
FastAPI 教學 – 請求與回應(Response 物件)
簡介
在使用 FastAPI 開發 API 時,最常與使用者互動的就是 Request 與 Response。雖然 FastAPI 內建的自動回傳機制已相當強大,但在實務開發中,我們常常需要自行控制回傳的 HTTP 狀態碼、標頭、內容類型,甚至是回傳純文字、二進位檔案或自訂的 JSON 結構。這時,fastapi.Response(以及其子類別)就派上用場。
本單元將深入探討 Response 物件 的用途與操作方式,讓你能在 FastAPI 中精準掌握回應的每一個細節,從而提升 API 的彈性與可維護性。
核心概念
1. 為什麼需要手動建立 Response
- 自訂狀態碼:預設回傳 200 OK,但有時需要回傳 201、202、204 或 4xx/5xx。
- 設定 Header:如 CORS、Cache-Control、Content-Disposition 等。
- 改變 Content-Type:返回
text/plain、application/xml、image/png等非 JSON 格式。 - 回傳二進位資料:檔案下載、圖片、PDF 等。
Tip:若只回傳純 JSON,直接回傳 Python 資料結構即可;若需要上述任何客製化,請使用
Response。
2. 基本使用方式
from fastapi import FastAPI, Response
app = FastAPI()
@app.get("/hello")
def hello():
# 直接回傳字串,FastAPI 會自動包成 JSON
return "Hello, world!"
上述程式會自動把字串包成 JSON ("Hello, world!") 並回傳 application/json。若要改成純文字回應:
@app.get("/plain")
def plain_text():
return Response(content="Hello, plain text!", media_type="text/plain")
Response 的建構子最常用的參數:
| 參數 | 說明 |
|---|---|
content |
要回傳的原始資料(字串、bytes)。 |
status_code |
HTTP 狀態碼,預設 200。 |
media_type |
Content-Type,例如 application/json、text/html。 |
headers |
其他自訂 Header,型別為 dict。 |
3. 子類別 – JSONResponse, HTMLResponse, StreamingResponse
FastAPI 為常見需求提供了專屬的子類別,讓程式碼更具可讀性。
3.1 JSONResponse
即使回傳的是字典或列表,FastAPI 會自動使用 JSONResponse。手動使用可自行設定狀態碼或 Header。
from fastapi.responses import JSONResponse
@app.get("/custom-json")
def custom_json():
data = {"msg": "自訂 JSON", "code": 123}
return JSONResponse(content=data, status_code=201, headers={"X-My-Header": "value"})
3.2 HTMLResponse
回傳 HTML 頁面時使用。
from fastapi.responses import HTMLResponse
@app.get("/page", response_class=HTMLResponse)
def html_page():
html_content = """
<html>
<head><title>FastAPI HTML</title></head>
<body><h1>Hello, HTML!</h1></body>
</html>
"""
return HTMLResponse(content=html_content, status_code=200)
3.3 StreamingResponse
適合大檔案或即時串流(如影片、CSV 匯出)。
from fastapi.responses import StreamingResponse
import io
@app.get("/stream-csv")
def stream_csv():
def generate():
yield "col1,col2\n"
for i in range(1, 101):
yield f"{i},{i*2}\n"
stream = io.StringIO()
return StreamingResponse(generate(), media_type="text/csv")
4. 進階範例
4.1 回傳檔案下載(FileResponse)
from fastapi.responses import FileResponse
import pathlib
@app.get("/download")
def download_file():
file_path = pathlib.Path("files/report.pdf")
# 讓瀏覽器下載而不是直接開啟
return FileResponse(
path=file_path,
media_type="application/pdf",
filename="report.pdf",
headers={"Content-Disposition": "attachment; filename=report.pdf"}
)
4.2 設定多個 Header
@app.get("/headers")
def multiple_headers():
custom_headers = {
"X-Request-ID": "12345",
"Cache-Control": "no-store",
}
return Response(
content="帶有多個自訂 Header",
media_type="text/plain",
headers=custom_headers
)
4.3 動態決定回傳類型
from fastapi import Query
from fastapi.responses import JSONResponse, PlainTextResponse
@app.get("/dynamic")
def dynamic_response(fmt: str = Query("json")):
data = {"msg": "這是動態回應"}
if fmt == "text":
return PlainTextResponse(content="純文字回應")
return JSONResponse(content=data)
4.4 使用 Response 直接回傳二進位資料
@app.get("/image")
def get_image():
with open("static/logo.png", "rb") as f:
img_bytes = f.read()
return Response(content=img_bytes, media_type="image/png")
4.5 結合 status_code 與 Response 產生 204 No Content
@app.delete("/items/{item_id}")
def delete_item(item_id: int):
# 假設刪除成功
return Response(status_code=204) # No Content
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方式 |
|---|---|---|
忘記設定 media_type |
回傳二進位資料卻仍使用預設 application/json,會導致瀏覽器無法正確解碼。 |
明確指定 media_type(如 image/png、application/pdf)。 |
直接回傳 bytes 而未使用 Response |
FastAPI 會把 bytes 轉成 JSON,造成錯誤。 |
包裝成 Response(content=bytes_data, media_type=…)。 |
| 大量 Header 造成效能下降 | 每次建立大量自訂 Header 會增加 CPU 與記憶體負擔。 | 僅在必要時加入 Header,或使用 middleware 統一處理。 |
| 狀態碼與回傳內容不一致 | 例如回傳 200 OK 但內容是錯誤訊息。 | 依照業務需求正確對應狀態碼(如 404、400、422)。 |
忘記 await 非同步檔案 I/O |
在非同步路由中使用同步 open() 會阻塞事件迴圈。 |
使用 aiofiles 或 async with 讀寫檔案。 |
最佳實踐
- 預設使用
JSONResponse:除非有特別需求,保持 API 回傳一致的 JSON 結構。 - 集中管理 Header:使用 middleware 或 dependency 統一設定 CORS、Cache-Control 等。
- 利用子類別提升可讀性:如返回 HTML 時直接使用
HTMLResponse,讓程式語意更清晰。 - 明確回傳狀態碼:使用
status_code=參數或Response(status_code=…),避免隱式 200。 - 檔案與串流使用
FileResponse/StreamingResponse:避免一次載入過大檔案導致記憶體爆炸。
實際應用場景
| 場景 | 需求 | 建議的 Response 使用方式 |
|---|---|---|
| 前端表單提交後需要顯示成功訊息 | 回傳 201 Created 並提供新資源的 URL | JSONResponse(content={"id": new_id}, status_code=201, headers={"Location": f"/items/{new_id}"}) |
| 下載報表(PDF / CSV) | 讓使用者下載檔案,並設定檔名 | FileResponse(path=..., media_type="application/pdf", filename="report.pdf") |
| 即時串流大資料(如影片) | 逐塊傳送資料,降低記憶體佔用 | StreamingResponse(generator(), media_type="video/mp4") |
| 健康檢查 API | 只需要回傳 204 No Content | Response(status_code=204) |
| 錯誤回報 | 回傳錯誤代碼與自訂訊息 | JSONResponse(content={"detail": "Invalid token"}, status_code=401) |
總結
- Response 物件 是 FastAPI 中掌控 HTTP 回應的核心工具,讓我們能自行設定 狀態碼、Header、Content-Type,以及回傳 純文字、二進位、檔案或串流。
- 透過 子類別(
JSONResponse,HTMLResponse,FileResponse,StreamingResponse等)可以寫出更具語意且易於維護的程式碼。 - 在實務開發中,常見的陷阱包括忘記設定
media_type、直接回傳bytes、以及狀態碼與內容不匹配。遵守 最佳實踐(集中管理 Header、明確使用狀態碼、適時使用子類別)能降低錯誤率並提升效能。 - 了解不同 應用場景 後,選擇最合適的 Response 類型,能讓 API 更符合前端需求與業務流程。
掌握了 Response 的使用,你就能在 FastAPI 中自如地處理各種回傳需求,寫出彈性十足、效能優化的 API。祝你開發順利,持續探索 FastAPI 的更多可能!