本文 AI 產出,尚未審核
FastAPI 路由(Routing)
主題:路由前綴(APIRouter prefix)
簡介
在開發大型 API 時,路由的組織與維護往往是最容易變得雜亂的部份。若每個 endpoint 都直接寫在 main.py,隨著功能增多,檔案會迅速變得難以閱讀、測試與重構。FastAPI 為此提供了 APIRouter,讓我們可以把相關的路由切分成模組,並透過 prefix 給予統一的 URL 前綴,讓 API 結構更清晰、路徑管理更一致。
本文將說明 APIRouter prefix 的概念、使用方式與實務上的好處,並提供多個可直接套用的範例,協助你在專案中快速導入這項功能。
核心概念
1️⃣ 為什麼需要 prefix?
- 統一命名空間:將同一類別的資源(例如使用者、商品)集中在同一段路徑下,如
/users/*、/products/*。 - 降低重複程式碼:不必在每個路由上手動寫相同的字串,減少打錯或遺漏的機會。
- 方便版本管理:只要在根路由加上版本前綴(
/v1、/v2),所有子路由自動跟隨,升級時更省事。
2️⃣ 基本語法
from fastapi import FastAPI, APIRouter
app = FastAPI()
router = APIRouter(
prefix="/users", # <--- 這裡設定前綴
tags=["users"], # 用於自動產生的 API 文件分組
responses={404: {"description": "Not found"}}
)
@router.get("/")
async def list_users():
return [{"id": 1, "name": "Alice"}]
@router.get("/{user_id}")
async def get_user(user_id: int):
return {"id": user_id, "name": "Bob"}
app.include_router(router)
prefix只會在 include 時套用,原始路由定義仍保持簡潔。- 若在
router中再加上子路由(如@router.get("/profile")),最終路徑會是/users/profile。
3️⃣ 多層路由前綴
FastAPI 允許 巢狀 APIRouter,讓你可以同時設定全局前綴與模組前綴:
api_v1 = APIRouter(prefix="/v1")
users_router = APIRouter(prefix="/users")
@users_router.get("/")
async def list_users():
...
api_v1.include_router(users_router) # 完整路徑 => /v1/users/
app.include_router(api_v1)
這樣的結構在 微服務 或 多版本 API 時特別有用。
4️⃣ 前綴與依賴(Dependencies)的結合
APIRouter 也支援 依賴注入,可以在前綴層級設定共用的依賴,所有子路由自動取得:
from fastapi import Depends, Header
def verify_token(x_token: str = Header(...)):
if x_token != "secret-token":
raise HTTPException(status_code=401, detail="Invalid token")
return x_token
secure_router = APIRouter(
prefix="/admin",
dependencies=[Depends(verify_token)]
)
@secure_router.get("/stats")
async def get_stats():
return {"users": 123, "orders": 456}
只要把 secure_router 加入 app,所有 /admin/* 路徑都會先執行 verify_token。
5️⃣ 範例彙總:從零建構一個分層 API
以下示範一個 完整的專案結構,包含三個子路由(users、items、orders),每個子路由都有自己的 prefix、tags,最外層再加上版本前綴:
project/
│
├─ main.py
├─ routers/
│ ├─ __init__.py
│ ├─ users.py
│ ├─ items.py
│ └─ orders.py
main.py
from fastapi import FastAPI
from routers import users, items, orders
app = FastAPI(title="Shop API")
api_v1 = APIRouter(prefix="/v1")
api_v1.include_router(users.router)
api_v1.include_router(items.router)
api_v1.include_router(orders.router)
app.include_router(api_v1)
routers/users.py
from fastapi import APIRouter
router = APIRouter(
prefix="/users",
tags=["users"]
)
@router.get("/", summary="取得所有使用者")
async def list_users():
return [{"id": 1, "name": "Alice"}]
@router.get("/{user_id}", summary="取得單一使用者")
async def get_user(user_id: int):
return {"id": user_id, "name": "Bob"}
routers/items.py
from fastapi import APIRouter, HTTPException
router = APIRouter(
prefix="/items",
tags=["items"]
)
items_db = {1: {"name": "Laptop"}, 2: {"name": "Phone"}}
@router.get("/", summary="列出所有商品")
async def list_items():
return items_db
@router.get("/{item_id}", summary="取得單一商品")
async def get_item(item_id: int):
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Item not found")
return items_db[item_id]
routers/orders.py
from fastapi import APIRouter, Depends
router = APIRouter(
prefix="/orders",
tags=["orders"]
)
def get_current_user():
# 假設從 token 取得使用者資訊
return {"id": 1, "name": "Alice"}
@router.post("/", summary="建立新訂單")
async def create_order(order: dict, user: dict = Depends(get_current_user)):
# 實作略...
return {"msg": "Order created", "user": user}
重點:只要在
main.py把api_v1包含進app,所有路徑自動變成/v1/users/*、/v1/items/*、/v1/orders/*,而且每個子路由的程式碼都保持 模組化、易於測試。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方式 |
|---|---|---|
忘記在 include_router 時保留前綴 |
若在子路由內再次設定 prefix,但在 include_router 時未傳入 prefix,會導致路徑不如預期。 |
統一在 APIRouter 建構子 設定 prefix,不要在 include_router 再次加入相同前綴。 |
| 前綴與路由參數衝突 | 例如 prefix="/users/{user_id}" 再寫 @router.get("/{item_id}"),會產生路徑 /users/{user_id}/{item_id},易混淆。 |
避免在前綴中使用參數,除非確實需要層級資源(如 /users/{user_id}/items),此時應清楚文件化。 |
| 過度嵌套 | 多層 APIRouter 嵌套雖然靈活,但過深會使除錯變難。 |
保持兩層以內(版本 + 功能),若需要更細分,可考慮在功能路由內使用 子路徑 而非再套 APIRouter。 |
| 依賴被覆寫 | 子路由的 dependencies 會 覆寫 父路由的同類型依賴,若不小心會失去全域驗證。 |
明確使用 dependencies= 參數,或在子路由內 Depends 時呼叫 parent_dependency。 |
| Swagger 文檔分組不一致 | 若 tags 未統一設定,Swagger 會把同一模組的路由分散。 |
在每個 APIRouter 建構時 指定 tags,確保 UI 上的分組清晰。 |
最佳實踐
- 模組化:每個業務領域(User、Item、Order)各自放在
routers/資料夾,保持main.py只做組合工作。 - 統一前綴:將版本號、語系、租戶等公共資訊放在最外層
APIRouter,子路由只負責功能前綴。 - 使用
tags與summary:讓自動產生的 OpenAPI 文件更易讀。 - 測試路由:利用
TestClient測試每個APIRouter,確保前綴不會因變更而斷裂。 - 文件化:在程式碼註解或 README 中說明每個前綴的意義,避免新加入的開發者產生疑惑。
實際應用場景
| 場景 | 前綴使用方式 | 為什麼適合 |
|---|---|---|
| 多租戶 SaaS 平台 | APIRouter(prefix="/{tenant_id}") + 各功能子路由 |
讓每個租戶的 API 完全隔離,只需在最外層加入租戶參數即可。 |
| API 版本管理 | APIRouter(prefix="/v1")、APIRouter(prefix="/v2") |
新增功能或變更行為時,只需新增新版本的路由,舊版仍可繼續服務。 |
| 微服務網關 | 在 API Gateway 中使用 APIRouter(prefix="/auth")、APIRouter(prefix="/payment") |
把不同微服務的入口統一在同一 FastAPI 應用內,減少部署成本。 |
| 手機與 Web 前端分流 | APIRouter(prefix="/mobile")、APIRouter(prefix="/web") |
針對不同客戶端提供客製化的回傳結構或驗證方式。 |
| 功能開關(Feature Flag) | APIRouter(prefix="/beta") 只在測試環境 include |
讓測試版功能僅在特定環境可見,正式環境不會暴露給使用者。 |
總結
APIRouter prefix是 FastAPI 用來 統一路徑命名、簡化程式碼、支援版本與多租戶 的強大工具。- 透過 模組化、層級前綴,我們可以在保持程式結構清晰的同時,快速擴充功能。
- 注意 避免前綴參數衝突、過度嵌套,並善用
tags、summary讓自動產生的文件更具可讀性。 - 在實務專案中,將 版本、租戶、功能 分別放在不同層級的
APIRouter,即可同時滿足 可維護性 與 彈性擴充 的需求。
掌握了路由前綴的使用,你的 FastAPI 專案將從「雜亂」變成「條理分明」,開發與維護都會變得更輕鬆。祝你在打造高效 API 的路上一路順風 🚀。