FastAPI 教學:在 OpenAPI / Swagger / ReDoc 中隱藏路由 (include_in_schema=False)
簡介
在開發 RESTful API 時,FastAPI 會自動為每一條路由產生 OpenAPI 規格,並以 Swagger UI 或 ReDoc 的方式呈現在文件頁面上。這對於 快速測試、與前端溝通 十分有幫助,卻也會帶來一個常見的需求:有些路由不希望出現在公開的 API 文件(例如內部測試用、健康檢查、管理介面或是僅供系統內部呼叫的端點)。
FastAPI 提供 include_in_schema=False 這個參數,讓開發者可以輕鬆控制哪些路由會被納入 OpenAPI 描述中。本文將從概念說明、實作範例、常見陷阱與最佳實踐,逐步帶你掌握 隱藏路由 的技巧,並說明在實務專案中如何運用。
核心概念
1. 為什麼要隱藏路由?
- 安全性:避免將管理或除錯用的端點暴露給外部使用者,降低被濫用的風險。
- 文件潔淨:讓 Swagger / ReDoc 僅呈現正式對外提供的 API,提升文件可讀性。
- 版本管理:舊版或即將棄用的端點可以暫時保留在程式碼中,但不顯示在文件裡,避免誤導使用者。
2. include_in_schema 的運作原理
FastAPI 在建立路由時,會把路由資訊(路徑、方法、參數、回傳模型等)寫入 OpenAPI schema。當 include_in_schema=False 時,FastAPI 仍然會註冊這條路由,讓它能正常被呼叫,但 不會把它寫入 schema,因此 Swagger UI、ReDoc 以及自動產生的 /openapi.json 都不會看到它。
重點:
- 路由仍然可用,只是 文件中隱形。
- 這個參數只能在 路由裝飾器(
@app.get、@router.post等)或 APIRouter 建立時設定。
3. 基本語法
@app.get("/hidden-endpoint", include_in_schema=False)
async def hidden_endpoint():
return {"msg": "這條路由不會出現在 OpenAPI 文件中"}
若使用 APIRouter,同樣適用:
router = APIRouter()
@router.post("/admin/clean-db", include_in_schema=False)
async def clean_database():
# 只允許系統內部呼叫
...
程式碼範例
以下示範 5 個常見情境,說明如何在不同層級、不同需求下使用 include_in_schema=False。
範例 1:隱藏健康檢查端點
健康檢查 (health check) 常被監控系統呼叫,不需要出現在 API 文件。
from fastapi import FastAPI
app = FastAPI()
@app.get("/healthz", include_in_schema=False, tags=["monitor"])
async def health_check():
"""
系統健康檢查,僅供監控工具使用。
"""
return {"status": "ok"}
小技巧:使用
tags可以在 Swagger UI 中仍然為這類隱藏端點分類,方便內部測試。
範例 2:隱藏測試用的 CRUD
開發階段常會寫一些僅供測試的 API,例如快速插入測試資料。
@app.post("/test/create-user", include_in_schema=False)
async def create_test_user(username: str, password: str):
"""
只在測試環境開啟,正式上線請務必關閉或移除。
"""
# 假設有一個 User model
user = User.create(username=username, password=password)
return {"id": user.id, "username": user.username}
提醒:部署到正式環境前,務必檢查
include_in_schema=False的端點是否已被移除或加上環境限制。
範例 3:使用 APIRouter 隱藏管理路由
大型專案常把路由拆分成多個 APIRouter,以下示範如何在子路由層級隱藏。
from fastapi import APIRouter, Depends, HTTPException, status
admin_router = APIRouter(prefix="/admin", tags=["admin"])
def verify_admin(token: str = Depends(oauth2_scheme)):
if token != "super-secret-admin-token":
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
return True
@admin_router.delete("/users/{user_id}", include_in_schema=False, dependencies=[Depends(verify_admin)])
async def delete_user(user_id: int):
"""
只允許系統管理員呼叫的刪除使用者端點。
"""
# 刪除邏輯...
return {"msg": f"User {user_id} 已被刪除"}
在主程式中掛載:
app.include_router(admin_router)
最佳實踐:配合 依賴注入(
Depends)驗證權限,確保隱藏端點不會被未授權的使用者誤用。
範例 4:條件式隱藏(依環境變數)
有時候想在 開發環境 顯示端點、正式環境 隱藏。可以利用環境變數動態設定。
import os
from fastapi import FastAPI
app = FastAPI()
SHOW_DEBUG_ENDPOINT = os.getenv("SHOW_DEBUG_ENDPOINT", "false").lower() == "true"
@app.get(
"/debug/info",
include_in_schema=SHOW_DEBUG_ENDPOINT, # 依環境決定是否納入文件
tags=["debug"]
)
async def debug_info():
"""
提供系統除錯資訊,僅在開發環境開啟。
"""
return {"debug": "some internal state"}
注意:即使
include_in_schema=False,端點仍然可被呼叫。若要完全關閉,還需要在程式碼裡加入條件判斷或使用 路由移除 的方式。
範例 5:自訂 OpenAPI 產出,排除特定前綴
如果專案中有大量以 _internal 為前綴的路由,想一次性排除,可透過 自訂 OpenAPI 產生函式。
from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi
app = FastAPI()
@app.get("/public/items")
async def read_items():
return [{"item_id": 1}]
@app.get("/_internal/metrics")
async def internal_metrics():
return {"cpu": "10%"}
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title="My API",
version="1.0.0",
routes=app.routes,
)
# 移除所有 path 以 "/_internal" 開頭的項目
paths_to_remove = [path for path in openapi_schema["paths"] if path.startswith("/_internal")]
for path in paths_to_remove:
del openapi_schema["paths"][path]
app.openapi_schema = openapi_schema
return app.openapi_schema
app.openapi = custom_openapi
重點:此方式不需要在每個路由上設定
include_in_schema=False,適合 大量內部端點 的情況。但仍建議保留include_in_schema=False作為防呆機制,避免忘記在新路由中加入前綴。
常見陷阱與最佳實踐
| 陷阱 | 可能的後果 | 解決方案或最佳實踐 |
|---|---|---|
僅使用 include_in_schema=False,卻忘記權限驗證 |
攻擊者仍可直接呼叫端點,造成安全漏洞。 | 搭配 Depends、OAuth2、API Key 等驗證機制。 |
| 在正式環境忘記關閉測試端點 | 測試資料外洩或資源被濫用。 | 使用 環境變數 或 設定檔 控制是否載入特定路由。 |
大量手動設定 include_in_schema=False,忘記一條 |
文件不一致,使用者會看到未完成的端點。 | 透過 自訂 OpenAPI 或 路由前綴過濾 方式一次性排除。 |
| 將路由隱藏後,忘記在 Swagger UI 中說明 | 開發者找不到端點的說明,導致溝通成本上升。 | 在程式碼註解或 README 中標註「隱藏端點」的用途與呼叫方式。 |
| 隱藏端點卻仍被自動生成的測試腳本抓取 | CI/CD 測試失敗或測試過度依賴內部 API。 | 在測試腳本中加入 環境檢查,只在開發/測試環境執行。 |
最佳實踐總結
- 永遠結合驗證:隱藏不等於安全,仍需權限控管。
- 環境分離:使用環境變數或設定檔,讓隱藏端點只在需要的環境開啟。
- 文件說明:即使不顯示在 Swagger,也要在專案文件或內部 wiki 說明端點的目的與使用方式。
- 統一管理:如果隱藏端點很多,考慮使用 自訂 OpenAPI 或 路由前綴 方式一次性過濾,減少遺漏。
- CI/CD 檢查:在部署流程加入檢查腳本,確保正式環境不會意外暴露測試或管理端點。
實際應用場景
1. 微服務健康檢查
在 Kubernetes 中,livenessProbe 與 readinessProbe 會定期呼叫 /healthz。將此端點設為 include_in_schema=False,即可避免 Swagger UI 充斥與業務無關的路徑。
2. 內部管理介面
企業後台常有 資料清理、批次匯入 等功能,只允許系統管理員使用。透過 include_in_schema=False 隱藏,同時加上 JWT 或 OAuth2 權限驗證,確保只有授權使用者能呼叫。
3. 測試專用端點
在 CI pipeline 中,有時需要快速建立測試資料或重置資料庫。寫一組 /test/* 的端點,並在 include_in_schema=False 下,只在 pytest 或 GitHub Actions 中開啟,避免正式使用者看到。
4. 多版本 API 管理
舊版 API 正在逐步淘汰,仍需保留相容性。可以把舊版路由標記為 include_in_schema=False,讓新使用者只看到最新版本,同時保留舊版路由供老系統呼叫。
5. 客製化文件產出
有些公司希望在公開文件中隱藏 內部服務間的 RPC 端點,只在內部文件或 Confluence 中說明。透過 include_in_schema=False,配合自訂 OpenAPI,輕鬆產出兩套文件:一套對外公開,一套內部使用。
總結
include_in_schema=False是 FastAPI 提供的簡潔機制,讓開發者可以在不影響路由功能的前提下,控制 OpenAPI 文件的呈現。- 隱藏路由不等於安全,必須配合 授權、環境控制 與 程式碼註解,才能真正降低風險。
- 在大型專案中,建議使用 自訂 OpenAPI 或 路由前綴過濾 方式一次性管理,避免遺漏。
- 透過本文的 實務範例、常見陷阱 與 最佳實踐,你可以快速在自己的 FastAPI 專案裡,將測試、管理或內部端點妥善隱藏,讓 API 文件更乾淨、系統更安全。
行動建議:在你的下一個 FastAPI 專案中,先為所有不需要公開的端點加上
include_in_schema=False,再根據實際需求加入驗證與環境條件,逐步打造既 易於測試 又 安全可靠 的 API 生態系。祝開發順利!