FastAPI 教學 – 路由(Routing)
主題:路由參數(Path parameters)
簡介
在 FastAPI 中,路由(Route)是 API 與外部世界溝通的入口,而 路由參數(Path parameters)則是讓同一條路由能根據不同的「路徑變數」執行不同的邏輯。
不論是取得單筆商品資料、編輯特定使用者或是刪除某筆記錄,都會需要把 資源的唯一識別碼(如 id、slug)以路徑參數的形式帶入。
掌握路由參數的寫法與驗證機制,不僅能讓 API 語意更清晰,還能利用 FastAPI 內建的型別檢查與自動文件產生,大幅提升開發效率與可維護性。本文將從概念說明、實作範例、常見陷阱到最佳實踐,完整介紹如何在 FastAPI 中使用路由參數。
核心概念
1. 基本語法
在 FastAPI 中,路由參數是以大括號 {} 包住的路徑片段,並且可以直接在路由函式的參數列表中聲明對應的變數名稱與型別:
from fastapi import FastAPI
app = FastAPI()
@app.get("/users/{user_id}")
async def read_user(user_id: int):
"""
取得單一使用者資料
- `user_id` 會自動被 FastAPI 轉換成 int
- 若傳入非整數,會回傳 422 錯誤
"""
return {"user_id": user_id, "name": f"User{user_id}"}
{user_id}:路徑參數的名稱。user_id: int:在函式參數上加上型別註解,FastAPI 會自動完成 型別轉換 與 驗證。
重點:只要路徑參數名稱與函式參數名稱相同,即可自動對應;若名稱不一致,則需要使用
Path物件手動映射。
2. 使用 Path 進階設定
Path 允許我們為參數設定 預設值、驗證規則、說明文字,這些資訊會直接反映在自動產生的 OpenAPI 文件中。
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(
item_id: int = Path(..., title="商品 ID", gt=0, description="必須是正整數")
):
"""
取得商品資訊
- `gt=0` 代表只能接受大於 0 的數字
- `...` 表示此參數為必填
"""
return {"item_id": item_id, "price": 199}
title、description:文件說明。gt、lt、ge、le:數值範圍限制。regex:字串符合正則表達式。
3. 多個路由參數
同一條路由可以同時接受多個參數,順序與名稱皆依路徑定義而定。
@app.get("/users/{user_id}/orders/{order_id}")
async def read_user_order(user_id: int, order_id: str):
"""
取得特定使用者的特定訂單
- `user_id` 為整數
- `order_id` 為字串,可自行加入 regex 驗證
"""
return {"user_id": user_id, "order_id": order_id}
4. 參數型別的多樣性
FastAPI 支援多種型別,包括 int、float、bool、datetime、UUID 等。以下示範 datetime 與 UUID 的使用:
from datetime import datetime
from uuid import UUID
@app.get("/events/{event_date}")
async def read_event(event_date: datetime):
"""
取得特定日期的活動資訊
- 需要符合 ISO 8601 格式,例如 2023-09-01T10:00:00
"""
return {"event_date": event_date.isoformat()}
@app.get("/files/{file_id}")
async def read_file(file_id: UUID):
"""
以 UUID 取得檔案資訊
"""
return {"file_id": str(file_id)}
FastAPI 會自動把字串轉換成對應的 Python 物件,若格式不符則回傳 422 Unprocessable Entity。
5. 路徑參數與查詢參數的差異
| 類型 | 定義位置 | 用途 | 範例 |
|---|---|---|---|
| 路徑參數 | URL 路徑 (/users/{id}) |
標示資源唯一識別碼,必填 | /users/123 |
| 查詢參數 | URL 查詢字串 (?page=2) |
用於過濾、分頁、排序等 可選 條件 | /users?page=2 |
技巧:當參數是 資源的唯一鍵 時,請使用路徑參數;若是 過濾條件,則使用查詢參數。
程式碼範例(實用範例 5 個)
範例 1:簡易的 CRUD – 取得單筆資料
@app.get("/books/{book_id}")
async def get_book(book_id: int = Path(..., gt=0)):
"""
取得指定書本的資訊
"""
# 假設有一個資料庫查詢函式
book = {"id": book_id, "title": f"Book {book_id}"}
return book
範例 2:使用正則驗證的字串參數
@app.get("/users/{username}")
async def get_user(
username: str = Path(..., regex="^[a-zA-Z0-9_]{3,20}$", title="使用者名稱")
):
"""
僅接受英數與底線,長度 3~20 的使用者名稱
"""
return {"username": username}
範例 3:多參數與型別混合
@app.get("/orders/{order_id}/items/{item_id}")
async def get_order_item(
order_id: UUID,
item_id: int = Path(..., ge=1, description="商品編號,必須大於等於 1")
):
"""
依照訂單 UUID 與商品編號取得明細
"""
return {"order_id": str(order_id), "item_id": item_id}
範例 4:日期時間參數 + 查詢參數結合
from fastapi import Query
from datetime import date
@app.get("/reports/{report_date}")
async def get_report(
report_date: date,
include_details: bool = Query(False, description="是否包含詳細資料")
):
"""
取得特定日期的報表,`include_details` 為可選的查詢參數
"""
report = {"date": report_date.isoformat(), "summary": "簡要報表"}
if include_details:
report["details"] = "這裡是詳細資料"
return report
範例 5:自訂路徑參數類別(使用 Pydantic)
from pydantic import BaseModel, conint
class PageInfo(BaseModel):
page: conint(ge=1) = 1 # 必須 >= 1
size: conint(gt=0, le=100) = 10 # 1~100
@app.get("/products/{category}/page")
async def list_products(category: str, info: PageInfo = Depends()):
"""
分頁列出某類別的商品,使用 Pydantic 進行驗證
"""
return {
"category": category,
"page": info.page,
"size": info.size,
"items": [] # 假資料
}
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方式 |
|---|---|---|
| 路徑參數未宣告型別 | 預設會當成 str,但容易在後續運算時出現型別錯誤。 |
明確寫出型別(int、UUID 等),讓 FastAPI 自動驗證。 |
| 參數名稱不一致 | 路徑 {user_id} 與函式參數 uid 不同,會導致 404。 |
使用 Path(..., alias="user_id") 或保持名稱一致。 |
| 過寬的路由衝突 | 兩條路由如 /items/{id} 與 /items/latest 會產生衝突。 |
把固定路由放在前面,或使用更精確的正則限制。 |
| 未處理 422 錯誤 | 使用者傳入不合法的參數會直接回 422,若未捕捉會讓前端不易除錯。 | 使用 Exception Handlers 統一格式化錯誤回應。 |
| 大量路徑參數導致 URL 雜亂 | 超過 3~4 個參數會影響可讀性與維護。 | 考慮改用 查詢參數 或 請求主體(body)。 |
最佳實踐
- 盡量使用型別註解:讓 FastAPI 自動完成轉換與驗證。
- 為每個參數加上說明 (
title、description):有助於自動文件的可讀性。 - 使用
Path的驗證條件(gt、lt、regex)代替手寫檢查。 - 保持路由的語意清晰:資源層級使用
/resource/{id},操作(如activate)則放在子路徑或使用 HTTP 方法。 - 統一錯誤回應格式:搭配
HTTPException或自訂的ExceptionHandler,提供前端友好的錯誤訊息。
實際應用場景
| 場景 | 為何使用路徑參數 | 範例路由 |
|---|---|---|
| 電商系統的商品細節 | 商品編號是唯一鍵,適合放在路徑中。 | /products/{product_id} |
| 社群平台的使用者檔案 | 使用者名稱或 UUID 為資源標識。 | /users/{username}、/users/{user_id} |
| 多租戶 SaaS | 租戶代碼是必須的前置條件。 | /tenants/{tenant_id}/reports/{year} |
| 文件下載服務 | 檔案的 UUID 或 hash 必須直接映射。 | /files/{file_hash} |
| 時間序列資料查詢 | 日期或時間戳記作為路徑參數,語意更直觀。 | /metrics/{date} |
在上述情況下,使用路徑參數不僅讓 API URL 結構更具可讀性,也能利用 FastAPI 的 自動驗證 減少手動錯誤檢查,提升開發與除錯效率。
總結
- 路由參數是 FastAPI 中最基礎且最常用的功能之一,透過大括號
{}定義、型別註解與Path物件的進階設定,我們可以快速建立 型別安全、文件完整的 API。 - 充分利用 數值/字串驗證、正則表達式、UUID、datetime 等內建型別,可讓 API 在收到不合法請求時即時回 422,減少後端的防呆程式碼。
- 在設計路由時應遵守 語意清晰、層級分明 的原則,避免過多路徑參數或路由衝突,並搭配 例外處理 讓錯誤訊息更友善。
掌握這些概念與實作技巧後,你就能在 FastAPI 專案中自如地使用路由參數,打造 高可讀、易維護、功能完整 的 RESTful API。祝開發順利! 🚀