本文 AI 產出,尚未審核

FastAPI ─ 請求與回應(Request & Response)

回傳 JSON、HTML、純文字與自訂格式


簡介

在 Web 應用開發中,回傳正確的資料格式是 API 能否被前端、行動裝置或第三方服務成功使用的關鍵。FastAPI 以其高效能與自動產生 OpenAPI 文件的特性,讓開發者可以輕鬆地定義不同的回應類型。
本篇文章將帶你一步一步了解 如何在 FastAPI 中回傳 JSON、HTML、純文字以及自訂格式,並說明背後的原理、常見陷阱與最佳實踐,讓你在實務專案中快速上手、寫出可維護且符合規範的 API。


核心概念

1. 回傳 JSON(預設回應)

FastAPI 預設使用 JSON 作為回應格式,這是因為 JSON 具備跨平台、易於解析的優勢。只要在路由函式中回傳 Python 的 dict、list、pydantic model,FastAPI 會自動將其序列化為 JSON。

範例 1️⃣:回傳純粹的 dict

from fastapi import FastAPI

app = FastAPI()

@app.get("/items")
def read_items():
    """回傳一組商品資訊,以 JSON 格式呈現。"""
    return {"items": ["Apple", "Banana", "Cherry"]}

說明return 的 dict 會在底層經過 jsonable_encoder 處理,確保所有可序列化的資料型別(如 datetime、Enum)都能正確轉換。

範例 2️⃣:使用 Pydantic Model

from fastapi import FastAPI
from pydantic import BaseModel
from typing import List

class Item(BaseModel):
    id: int
    name: str
    price: float

app = FastAPI()

@app.get("/item/{item_id}", response_model=Item)
def get_item(item_id: int):
    """依據 id 回傳單一商品資料,FastAPI 會自動產生 OpenAPI schema。"""
    sample = {"id": item_id, "name": "Laptop", "price": 1299.99}
    return sample

說明response_model 不僅限制回傳欄位,還會在 Swagger UI 中顯示模型結構,提升文件可讀性。

範例 3️⃣:回傳 List[Model]

@app.get("/users", response_model=List[Item])
def list_items():
    """回傳多筆商品,使用 List[Model] 讓文件自動展開。"""
    return [
        {"id": 1, "name": "Keyboard", "price": 49.9},
        {"id": 2, "name": "Mouse", "price": 19.9},
    ]

2. 回傳 HTML(模板渲染)

有時候我們需要直接在瀏覽器顯示 HTML 頁面(例如管理後台、簡易表單)。FastAPI 內建支援 Jinja2 等模板引擎,只要把 Response 類型指定為 HTMLResponse,並使用 templates.TemplateResponse 即可。

範例 4️⃣:設定 Jinja2 模板

from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
import pathlib

BASE_DIR = pathlib.Path(__file__).parent
templates = Jinja2Templates(directory=BASE_DIR / "templates")

app = FastAPI()

@app.get("/welcome", response_class=HTMLResponse)
def welcome(request: Request):
    """使用 Jinja2 渲染 welcome.html,傳入變數 name。"""
    return templates.TemplateResponse(
        "welcome.html",
        {"request": request, "name": "FastAPI 學員"}
    )

說明

  1. templates 指向 templates/ 目錄。
  2. TemplateResponse 需要傳入 request 物件,讓模板內可使用 url_for 等 FastAPI 內建工具。
  3. response_class=HTMLResponse 告訴 FastAPI 回傳 text/html Content-Type。

範例 5️⃣:動態表單

<!-- templates/form.html -->
<!DOCTYPE html>
<html lang="zh-TW">
<head>
    <meta charset="UTF-8">
    <title>簡易表單</title>
</head>
<body>
    <h1>Hello, {{ user }}!</h1>
    <form action="/submit" method="post">
        <input name="msg" placeholder="輸入訊息">
        <button type="submit">送出</button>
    </form>
</body>
</html>
@app.get("/form", response_class=HTMLResponse)
def get_form(request: Request):
    return templates.TemplateResponse("form.html", {"request": request, "user": "Alice"})

3. 回傳純文字(Plain Text)

在某些情境(如 health check、簡易 API)只需要回傳 純文字。FastAPI 提供 PlainTextResponse,只要在路由上設定 response_class 即可。

範例 6️⃣:健康檢查端點

from fastapi.responses import PlainTextResponse

@app.get("/health", response_class=PlainTextResponse)
def health_check():
    """返回簡單文字,讓容器 Orchestrator 能快速判斷服務狀態。"""
    return "OK"

說明:此端點回傳 text/plain; charset=utf-8,不會產生 JSON 包裝,適合 Nginx、Kubernetes liveness probe 使用。


4. 自訂格式(Binary、CSV、XML、PDF …)

FastAPI 允許 自訂回應類型,只要繼承 Response 並設定正確的 media_type,即可傳遞二進位資料或特定檔案格式。

範例 7️⃣:回傳 CSV 檔

from fastapi.responses import StreamingResponse
import csv
import io

@app.get("/export/csv")
def export_csv():
    """將資料即時轉成 CSV 並以串流方式回傳。"""
    data = [
        {"id": 1, "name": "Alice"},
        {"id": 2, "name": "Bob"},
    ]

    def iter_csv():
        output = io.StringIO()
        writer = csv.DictWriter(output, fieldnames=["id", "name"])
        writer.writeheader()
        for row in data:
            writer.writerow(row)
            yield output.getvalue()
            output.seek(0)
            output.truncate(0)

    return StreamingResponse(iter_csv(),
                             media_type="text/csv",
                             headers={"Content-Disposition": "attachment; filename=users.csv"})

說明

  • 使用 StreamingResponse 可以在資料量大時避免一次性載入記憶體。
  • Content-Disposition 讓瀏覽器下載而非直接顯示。

範例 8️⃣:回傳 PDF(二進位)

from fastapi.responses import Response
from pathlib import Path

@app.get("/report/pdf")
def get_pdf():
    """讀取本機 PDF 檔案,回傳二進位資料,設定正確的 MIME。"""
    pdf_path = Path("static/report.pdf")
    pdf_bytes = pdf_path.read_bytes()
    return Response(content=pdf_bytes,
                    media_type="application/pdf",
                    headers={"Content-Disposition": "inline; filename=report.pdf"})

說明media_type="application/pdf" 告訴瀏覽器此回應為 PDF,inline 讓瀏覽器直接開啟,若改為 attachment 則會觸發下載。


常見陷阱與最佳實踐

陷阱 說明 解決方式
忘記設定 response_class 預設回傳 application/json,若返回 HTML 或純文字會被自動包成 JSON,導致前端解析失敗。 明確在路由裝飾器上加上 response_class=HTMLResponsePlainTextResponse 等。
返回非 JSON 可序列化的物件 直接回傳 datetimeDecimalbytes 會拋出 TypeError 使用 jsonable_encoder 或在 response_model 中自行定義 json_encoders
大檔案一次讀取導致記憶體暴衝 直接 return FileResponse("big.zip") 會一次載入全部檔案。 使用 StreamingResponse 搭配生成器或 aiofiles 逐塊讀取。
缺少 Content-Type 自訂格式時忘記設定 media_type,瀏覽器可能以文字方式顯示二進位資料。 始終在 Response/StreamingResponse 中指定正確的 MIME,例如 application/xmlimage/png
模板安全性 直接把使用者輸入渲染到 HTML,易受 XSS 攻擊。 使用 Jinja2 預設的自動 escaping,或手動過濾危險字元。

最佳實踐

  1. 統一回應模型:即使回傳純文字,也可以封裝成 { "message": "OK" },讓前端有一致的解析邏輯。
  2. 使用依賴注入(Depends):將常用的 Response 物件或檔案處理抽成可重用的依賴。
  3. 善用 HTTP 狀態碼:回傳成功時使用 200,錯誤時使用 4xx/5xx,並在 JSONResponse 中提供 detail 欄位。
  4. 設定 Cache-Control:對於不常變動的靜態檔案(如 PDF、圖片)加上 Cache-Control: public, max-age=86400,提升效能。
  5. 測試回應 Header:使用 TestClient 確認 Content-TypeContent-Disposition 等 header 正確。

實際應用場景

場景 需要的回應類型 為什麼選擇此類型
前端 SPA 與後端 API JSON 前端框架(Vue、React)以 fetch 解析 JSON,結構化資料最方便。
系統健康檢查 純文字 (PlainTextResponse) 輕量、快速,容器 Orchestrator 只要檢查字串 OK 即可。
報表下載(CSV、Excel) StreamingResponse + text/csv 大量資料即時產生,避免記憶體浪費,且瀏覽器會自動觸發下載。
PDF 合同、發票 Response + application/pdf 需要保留檔案格式,讓使用者直接在瀏覽器預覽或下載。
管理後台介面 HTML (HTMLResponse) + Jinja2 需要動態渲染頁面、表單與驗證訊息,HTML 最直觀。
第三方系統整合(XML) 自訂 Response + application/xml 某些舊系統仍使用 XML 作為資料交換格式。

總結

在 FastAPI 中,回傳不同資料格式 並不需要額外的套件或複雜的設定,只要掌握以下三點即可:

  1. 了解預設行為:未指定 response_class 時,FastAPI 會自動將 Python 物件序列化為 JSON。
  2. 正確宣告 response_classmedia_type:HTML、純文字、CSV、PDF…等,都有對應的 Response 類別或自訂 MIME。
  3. 結合依賴注入與 Pydantic Model:讓回應結構化、文件化,同時保持程式碼的可讀性與可測試性。

透過本文提供的 實作範例、常見陷阱與最佳實踐,你可以在任何 FastAPI 專案中快速切換回應格式,滿足前端 UI、第三方系統或內部工具的需求。未來只要再面對更複雜的二進位傳輸(如圖片、音訊)或自訂協議,概念仍然相同:選擇合適的 Response、設定正確的 MIME、確保資料可序列化,即可在高效能的 FastAPI 應用中游刃有餘。祝開發順利!