FastAPI - API 文件(OpenAPI / Swagger / ReDoc)
主題:自訂 OpenAPI Schema
簡介
在使用 FastAPI 建立 RESTful 服務時,框架會自動產生符合 OpenAPI(舊稱 Swagger)規範的文件。這份文件不僅讓前端、測試或第三方開發者能即時瀏覽與測試 API,還能直接匯出給 API 管理平台、API 閘道或自動產生 SDK。
然而,預設產生的 Schema 常常只能滿足「基本」的需求;在實務專案裡,我們常需要 補充說明、調整欄位型別、加入自訂的驗證規則或安全機制,才能讓文件真正反映業務邏輯。
本篇文章將從 概念、實作、常見陷阱與最佳實踐,一步步帶你完成 OpenAPI Schema 的自訂,讓你的 API 文件既完整又易於維護。
核心概念
1. OpenAPI 與 FastAPI 的關係
- OpenAPI:一套描述 HTTP API 的規範,定義路徑、請求/回應結構、認證方式等。
- FastAPI:在啟動時會自動根據路由、Pydantic 模型與型別提示產生符合 OpenAPI 3.0 的 JSON(或 YAML)文件,並提供 Swagger UI 以及 ReDoc 兩種前端介面。
重點:FastAPI 只會根據 程式碼 產生文件,若程式碼中缺少說明或型別資訊,文件也會缺少相應內容。
2. 為什麼需要自訂 Schema
| 場景 | 預設行為 | 需要自訂的原因 |
|---|---|---|
| 多語系錯誤訊息 | 只顯示英文 detail |
必須提供中文說明,提升使用者體驗 |
| 客製化欄位說明 | 只顯示欄位名稱 | 需要加入業務規則、格式範例 |
| 隱藏內部端點 | 全部端點皆曝光 | 某些測試或管理端點不應出現在公開文件 |
| 自訂安全機制 | 只支援 HTTP Basic | 需要 OAuth2、API Key、JWT 等多種方式 |
3. FastAPI 提供的自訂入口
app.openapi():覆寫此方法即可回傳自訂的 OpenAPI dict。app.openapi_schema:直接賦值一個已修改好的 dict,FastAPI 會直接使用它。APIRouter的include_in_schema參數:控制單一路由是否出現在文件中。Field(..., description="...")、Schema(... ):在 Pydantic 模型層面提供欄位說明、範例、驗證。
程式碼範例
以下範例均以 Python 3.9+、FastAPI 0.109 為基礎,使用 uvicorn 執行。
範例 1:最簡單的自訂 OpenAPI 標題與描述
from fastapi import FastAPI
app = FastAPI(
title="我的購物車 API",
version="1.0.0",
description="提供商品 CRUD、下單與付款功能的 RESTful 服務。",
)
@app.get("/items/{item_id}")
async def read_item(item_id: int):
"""
取得單一商品資訊
"""
return {"item_id": item_id, "name": "範例商品"}
說明:直接在
FastAPI()建構子裡設定title、version、description,即可讓 Swagger UI 與 ReDoc 顯示自訂的文件資訊。
範例 2:在 Pydantic 模型中加入欄位說明與範例
from pydantic import BaseModel, Field
class OrderItem(BaseModel):
product_id: int = Field(..., description="商品的唯一識別碼")
quantity: int = Field(
...,
gt=0,
description="購買數量,必須大於 0",
example=2,
)
note: str | None = Field(
None,
description="給商家的備註(可選)",
max_length=200,
example="請以禮物包裝",
)
重點:
Field的description會出現在 Swagger UI 的欄位說明,example則會成為 範例值,讓測試者更容易填寫正確的資料。
範例 3:覆寫 app.openapi() 以加入全域安全機制
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import APIKeyHeader
API_KEY_NAME = "X-My-Api-Key"
api_key_header = APIKeyHeader(name=API_KEY_NAME, auto_error=False)
def get_api_key(key: str = Depends(api_key_header)):
if key != "secret-key":
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Invalid API Key",
)
return key
app = FastAPI()
# ---------- 自訂 OpenAPI ----------
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = app.original_openapi()
# 加入全域安全定義
security_scheme = {
"ApiKeyAuth": {
"type": "apiKey",
"in": "header",
"name": API_KEY_NAME,
}
}
openapi_schema["components"]["securitySchemes"] = security_scheme
# 讓所有路由預設使用此安全機制
for path in openapi_schema["paths"].values():
for operation in path.values():
operation.setdefault("security", [{"ApiKeyAuth": []}])
app.openapi_schema = openapi_schema
return app.openapi_schema
app.original_openapi = app.openapi
app.openapi = custom_openapi
# -----------------------------------
@app.get("/secure-data", dependencies=[Depends(get_api_key)])
async def secure_data():
return {"msg": "這是受保護的資源"}
說明:
- 先保留原始
app.openapi(app.original_openapi),以免遺失 FastAPI 自動產生的基礎結構。- 在
components.securitySchemes中加入自訂的 API Key 定義。- 逐一把每個 operation(GET、POST…)的
security欄位設為ApiKeyAuth,讓 Swagger UI 自動顯示授權欄位。
範例 4:使用 include_in_schema=False 隱藏測試端點
@app.get("/health", include_in_schema=False)
async def health_check():
"""
用於容器或負載平衡器的健康檢查,不應出現在公開文件中
"""
return {"status": "ok"}
技巧:將
include_in_schema=False加在路由裝飾器上,該端點仍可被呼叫,但不會出現在 Swagger 或 ReDoc。
範例 5:為回應加入自訂的描述與範例(使用 Response、JSONResponse)
from fastapi.responses import JSONResponse
from typing import Literal
class OrderResponse(BaseModel):
order_id: int = Field(..., description="系統產生的訂單編號")
status: Literal["created", "failed"] = Field(..., description="訂單狀態")
message: str | None = Field(None, description="若失敗時的錯誤說明")
@app.post(
"/orders",
response_model=OrderResponse,
responses={
201: {
"description": "訂單建立成功",
"content": {
"application/json": {
"example": {
"order_id": 12345,
"status": "created",
"message": None,
}
}
},
},
400: {
"description": "參數驗證失敗",
"content": {
"application/json": {
"example": {"detail": "商品不存在"}
}
},
},
},
)
async def create_order(item: OrderItem):
# 假設執行業務邏輯
return JSONResponse(status_code=201, content={"order_id": 12345, "status": "created"})
重點:
responses參數允許為每個 HTTP 狀態碼自訂說明與範例,極大提升文件的可讀性與測試便利性。
常見陷阱與最佳實踐
| 陷阱 | 可能的後果 | 解決方案或最佳實踐 |
|---|---|---|
忘記保留 app.original_openapi |
自訂後失去 FastAPI 自動產生的路徑、模型資訊,文件變得不完整 | 在覆寫 app.openapi 前先 app.original_openapi = app.openapi,並在自訂函式中呼叫它 |
在 Field 中使用不支援的屬性(例如 example 只對 list 無效) |
Swagger UI 顯示空白或錯誤 | 依照 OpenAPI 規範確認屬性支援情況,必要時使用 schema_extra |
安全機制只在 UI 中顯示,實際路由未加 Depends |
前端測試時可通過 UI 授權,但真實呼叫仍會 403 | 同時在路由或全域 dependencies 中加入驗證函式 |
| 過度自訂導致文件難以維護 | 每次模型變更都需要手動同步文件 | 盡量利用 Pydantic 的自動說明與 example,僅在必要時才手動編寫 responses |
| 隱藏端點忘記同步測試腳本 | CI 測試仍會呼叫已隱藏的端點,產生 404 | 在測試套件中使用 include_in_schema=False 的端點時,直接呼叫其路徑,避免依賴自動生成的文件 |
最佳實踐小結
- 先以模型說明為主:所有欄位說明、範例、驗證規則盡量寫在 Pydantic 模型內。
- 集中管理全域安全:使用
app.openapi統一注入securitySchemes,避免每個路由重複寫。 - 分層自訂:
- 全域(title、version、security) →
FastAPI()/app.openapi - 路由層級(include_in_schema、dependencies) → 裝飾器參數
- 回應層級(responses、example) →
@app.<method>(..., response_model=..., responses={...})
- 全域(title、version、security) →
- 自動化測試:使用
TestClient讀取app.openapi(),比對關鍵欄位是否正確,確保文件與程式碼同步。
實際應用場景
企業內部微服務平台
- 每個服務都有 OAuth2 授權、API Key 兩套安全機制。透過自訂
app.openapi,一次性在文件中加入兩種securitySchemes,讓前端團隊只要在 Swagger UI 直接切換即可測試。
- 每個服務都有 OAuth2 授權、API Key 兩套安全機制。透過自訂
對外公開的 SaaS API
- 為了降低使用門檻,需提供 多語系的錯誤說明。利用
responses中的example與description,在不同語系的 UI(Swagger/Redoc)中顯示對應文字。
- 為了降低使用門檻,需提供 多語系的錯誤說明。利用
金融或醫療系統
- 某些端點僅限特定客戶端呼叫,必須 隱藏 在公開文件裡。使用
include_in_schema=False隱藏測試或內部管理端點,同時在 CI 中仍保有測試。
- 某些端點僅限特定客戶端呼叫,必須 隱藏 在公開文件裡。使用
自動產生 SDK
- 多數 SDK 產生工具(如 OpenAPI Generator)會依據 OpenAPI JSON 生成程式碼。若文件缺少欄位說明或安全定義,生成的 SDK 會缺少必要的註解或驗證。自訂 Schema 後,SDK 可自動帶入完整的型別與驗證邏輯。
總結
FastAPI 讓我們只寫少量程式碼就能得到完整的 OpenAPI 文件,但實務上往往需要 自訂,才能滿足業務、法規或使用者體驗的需求。
本文從 概念、實作(五個具體範例)以及 常見陷阱與最佳實踐,說明了如何:
- 在
FastAPI()建構子中設定全域資訊。 - 利用 Pydantic 的
Field為模型欄位加入說明與範例。 - 透過覆寫
app.openapi(),一次性注入全域 安全機制。 - 使用
include_in_schema=False隱藏不需要公開的端點。 - 為每個回應狀態碼自訂說明與範例,提升文件可讀性。
掌握這些技巧後,你的 API 文件將不再是「自動產生的草稿」,而是 完整、精準、符合業務需求 的開發與合作工具。未來在擴充微服務、發布 SDK,甚至遵循資安合規時,都能從這套自訂的 OpenAPI Schema 中受益。
祝你在 FastAPI 的開發旅程中,寫出 高品質、易維護 的 API 文件! 🚀