本文 AI 產出,尚未審核

FastAPI 教學 – 請求與回應(Response 物件)

簡介

在使用 FastAPI 開發 API 時,最常與使用者互動的就是 RequestResponse。雖然 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/plainapplication/xmlimage/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/jsontext/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_codeResponse 產生 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/pngapplication/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() 會阻塞事件迴圈。 使用 aiofilesasync with 讀寫檔案。

最佳實踐

  1. 預設使用 JSONResponse:除非有特別需求,保持 API 回傳一致的 JSON 結構。
  2. 集中管理 Header:使用 middlewaredependency 統一設定 CORS、Cache-Control 等。
  3. 利用子類別提升可讀性:如返回 HTML 時直接使用 HTMLResponse,讓程式語意更清晰。
  4. 明確回傳狀態碼:使用 status_code= 參數或 Response(status_code=…),避免隱式 200。
  5. 檔案與串流使用 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 的更多可能!