本文 AI 產出,尚未審核

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。 在測試腳本中加入 環境檢查,只在開發/測試環境執行。

最佳實踐總結

  1. 永遠結合驗證:隱藏不等於安全,仍需權限控管。
  2. 環境分離:使用環境變數或設定檔,讓隱藏端點只在需要的環境開啟。
  3. 文件說明:即使不顯示在 Swagger,也要在專案文件或內部 wiki 說明端點的目的與使用方式。
  4. 統一管理:如果隱藏端點很多,考慮使用 自訂 OpenAPI路由前綴 方式一次性過濾,減少遺漏。
  5. CI/CD 檢查:在部署流程加入檢查腳本,確保正式環境不會意外暴露測試或管理端點。

實際應用場景

1. 微服務健康檢查

在 Kubernetes 中,livenessProbereadinessProbe 會定期呼叫 /healthz。將此端點設為 include_in_schema=False,即可避免 Swagger UI 充斥與業務無關的路徑。

2. 內部管理介面

企業後台常有 資料清理、批次匯入 等功能,只允許系統管理員使用。透過 include_in_schema=False 隱藏,同時加上 JWT 或 OAuth2 權限驗證,確保只有授權使用者能呼叫。

3. 測試專用端點

在 CI pipeline 中,有時需要快速建立測試資料或重置資料庫。寫一組 /test/* 的端點,並在 include_in_schema=False 下,只在 pytestGitHub 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 生態系。祝開發順利!