本文 AI 產出,尚未審核
FastAPI – Query / Path / Body 參數處理:Default Value 與 Required 欄位
簡介
在 FastAPI 中,API 的介面往往透過 Query、Path、Body 三種方式接收資料。
對於前端或第三方服務呼叫 API 時,哪些參數是 必填、哪些可以有預設值,直接影響到 使用者體驗、錯誤處理 以及 文件生成(OpenAPI)。
本篇文章將深入說明:
- 如何在 FastAPI 中為 Query、Path、Body 欄位設定 預設值(default value)。
- 何時必須把欄位標記為 required,以及 FastAPI 如何自動產生驗證錯誤。
- 常見的陷阱與最佳實踐,並提供 3~5 個實用範例,協助你在真實專案中快速上手。
核心概念
1. FastAPI 參數的三大來源
| 來源 | 位置 | 常見用途 | 代表型別 |
|---|---|---|---|
| Path | URL 路徑(/items/{item_id}) |
唯一資源辨識 | int, str, UUID … |
| Query | URL 後面的 ?key=value |
篩選、分頁、開關 | int, str, bool, list … |
| Body | HTTP 請求的 JSON、Form、File 等 | 建立或更新資源 | Pydantic Model、單一欄位 |
重點:FastAPI 會根據參數所在的「位置」自動決定其驗證方式,開發者只要在函式簽名中加入正確的型別與 FastAPI 提供的依賴(
Path,Query,Body)即可。
2. 預設值(Default Value)
2.1 為何需要預設值?
- 降低前端負擔:不必每次呼叫都帶上所有參數。
- 提供合理的行為:例如分頁的
limit=10、offset=0。 - 自動生成 OpenAPI 文件:預設值會顯示在 Swagger UI 中,讓使用者一眼看出「可不提供」的欄位。
2.2 設定方式
from fastapi import FastAPI, Query, Path
app = FastAPI()
@app.get("/items/{item_id}")
def read_item(
item_id: int = Path(..., description="商品唯一 ID"),
q: str = Query("default-search", max_length=50, description="搜尋關鍵字"),
limit: int = Query(10, ge=1, le=100, description="每頁筆數")
):
"""
範例說明:
- `item_id` 為 Path 參數,使用 `...` 表示 *必填*(見下節)。
- `q` 有預設值 `"default-search"`,若未傳遞則使用此字串。
- `limit` 預設 10,且限制在 1~100 之間。
"""
return {"item_id": item_id, "q": q, "limit": limit}
說明
Path(... )中的...代表「此參數必填」;若改成Path(1)則表示預設值為1,同時變成 非必填。Query("default-search")直接在Query內給定預設值,即可讓參數變為 可選。
3. Required 欄位
3.1 何時必須標記為 required?
- 資源唯一識別碼(如
item_id、user_id) - 必須提供才能完成業務邏輯的欄位(例如建立使用者時的
email、password) - 沒有合理的預設值或預設值會導致錯誤的情況
3.2 使用 ...(Ellipsis)或 Required 參數
from fastapi import FastAPI, Body, Path
from pydantic import BaseModel, Field
app = FastAPI()
class UserCreate(BaseModel):
email: str = Field(..., description="使用者的 Email") # 必填
password: str = Field(..., min_length=8) # 必填
nickname: str = Field("匿名使用者", description="顯示名稱") # 有預設值 → 非必填
@app.post("/users")
def create_user(user: UserCreate = Body(...)):
"""
- `Body(... )` 表示整個模型是必填。
- `UserCreate` 內部使用 `Field(... )` 標示必填欄位。
"""
# 假設在此處寫入資料庫…
return {"msg": "使用者建立成功", "user": user}
關鍵:
- 在 Pydantic Model 中,
Field(... )或直接把變數寫成email: str(不給預設值)皆會被視為 required。- 若想把整個 Body 設為必填,只要在路由函式的參數上使用
Body(... )。
4. 多樣化的預設值寫法
4.1 使用 None 與 Optional
from typing import Optional
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/search")
def search(
q: Optional[str] = Query(None, description="搜尋關鍵字,若為 None 則返回全部"),
page: int = Query(1, ge=1)
):
"""
- `q` 設為 `None`,表示「如果前端沒帶,就回傳全部」。
- `Optional[str]` 讓型別檢查器允許 None。
"""
return {"q": q, "page": page}
4.2 動態預設值(使用函式)
import datetime
from fastapi import FastAPI, Query
app = FastAPI()
def current_year():
return datetime.datetime.now().year
@app.get("/reports")
def get_report(
year: int = Query(default_factory=current_year, description="報表年份,預設為今年")
):
"""
`default_factory` 允許在每次請求時重新計算預設值,
這對於時間相關的欄位非常有用。
"""
return {"year": year, "data": f"Report of {year}"}
備註:
default_factory只在 FastAPI 0.78+ 版本支援,若使用舊版請自行在路由內處理。
常見陷阱與最佳實踐
| 陷阱 | 可能的結果 | 解決方案 |
|---|---|---|
忘記加 ...,把必填欄位寫成有預設值 |
前端可以不提供,導致業務邏輯錯誤 | 使用 Path(... )、Query(... ) 或 Field(... ) 明確標示 |
預設值與型別不一致(例如 limit: int = Query("10")) |
FastAPI 會在啟動時拋出錯誤 | 確保預設值的型別與參數型別相符 |
| 使用可變物件(list、dict)作為預設值 | 多個請求共用同一個物件,導致資料污染 | 改用 default_factory 或在路由內自行產生新物件 |
把 None 當作預設值卻未使用 Optional |
Pydantic 會把 None 視為非法值 |
使用 Optional[T] 或 Union[T, None] |
| 過度依賴預設值,導致 API 文件不清楚 | 使用者無法快速了解哪些參數是必要的 | 在 Swagger UI 中加上 description,並在文件中註明 |
最佳實踐
明確標示必填與可選
- 必填 →
...或Field(... ) - 可選 → 提供合理的預設值或
None+Optional
- 必填 →
為每個參數加上
description,讓自動產生的 OpenAPI 文件更具可讀性。使用型別限制(
gt、lt、ge、le、max_length…)在參數層面提前攔截錯誤。避免在模型層面使用可變物件作為預設值,改用
default_factory或在路由內初始化。測試所有組合:必填、缺省、錯誤型別,確保 FastAPI 的自動驗證行為符合預期。
實際應用場景
1. 分頁與搜尋 API
@app.get("/products")
def list_products(
page: int = Query(1, ge=1, description="目前頁碼,從 1 開始"),
size: int = Query(20, ge=1, le=100, description="每頁顯示筆數"),
q: Optional[str] = Query(None, description="關鍵字搜尋,若為 None 則列出全部")
):
"""
- `page`、`size` 有預設值,讓前端可以省略。
- `q` 為 Optional,提供彈性的搜尋條件。
"""
# 假設在此呼叫資料庫...
return {"page": page, "size": size, "query": q}
2. 使用者註冊(Body 必填欄位)
class RegisterForm(BaseModel):
username: str = Field(..., min_length=3, max_length=20)
password: str = Field(..., min_length=8)
email: EmailStr = Field(..., description="有效的 Email")
referral_code: Optional[str] = Field(None, description="推薦碼,非必填")
@app.post("/auth/register")
def register_user(form: RegisterForm = Body(...)):
"""
- 所有欄位皆使用 `Field(... )` 表示必填,除了 referral_code。
- FastAPI 會自動在 Swagger UI 上把必填欄位標示為紅星。
"""
# 處理註冊邏輯...
return {"msg": "註冊成功", "user": form.username}
3. 動態預設值:今日日期的報表
from datetime import date
@app.get("/sales")
def sales_report(
day: date = Query(default_factory=date.today, description="報表日期,預設為今天")
):
"""
使用 `default_factory`,每次請求都會得到最新的日期。
"""
# 產生報表...
return {"date": day.isoformat(), "sales": 12345}
總結
- Default Value 能讓 API 更友善、文件更完整,但必須確保型別正確且不使用可變物件作為預設值。
- Required 欄位 必須透過
...(Ellipsis)或Field(... )明確宣告,讓 FastAPI 在請求階段即自動產生驗證錯誤。 - 利用 FastAPI 的依賴(Path、Query、Body) 搭配 Pydantic 的模型與欄位設定,既能保證資料正確性,又能自動產生 OpenAPI 文檔,減少手動維護成本。
- 實務上,常見的 分頁、搜尋、註冊 等情境,都能透過上述技巧快速完成參數驗證與預設值設定。
掌握了 default value 與 required 的概念後,你就能在設計 API 時,既保留彈性又避免錯誤,讓前端開發者與使用者都有更好的體驗。祝你在 FastAPI 的開發旅程中,寫出更乾淨、更可靠的介面!