本文 AI 產出,尚未審核

FastAPI – Query / Path / Body 參數處理:Default Value 與 Required 欄位


簡介

FastAPI 中,API 的介面往往透過 Query、Path、Body 三種方式接收資料。
對於前端或第三方服務呼叫 API 時,哪些參數是 必填、哪些可以有預設值,直接影響到 使用者體驗錯誤處理 以及 文件生成(OpenAPI)。

本篇文章將深入說明:

  1. 如何在 FastAPI 中為 Query、Path、Body 欄位設定 預設值(default value)
  2. 何時必須把欄位標記為 required,以及 FastAPI 如何自動產生驗證錯誤。
  3. 常見的陷阱與最佳實踐,並提供 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=10offset=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_iduser_id
  • 必須提供才能完成業務邏輯的欄位(例如建立使用者時的 emailpassword
  • 沒有合理的預設值或預設值會導致錯誤的情況

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 使用 NoneOptional

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,並在文件中註明

最佳實踐

  1. 明確標示必填與可選

    • 必填 → ...Field(... )
    • 可選 → 提供合理的預設值或 None + Optional
  2. 為每個參數加上 description,讓自動產生的 OpenAPI 文件更具可讀性。

  3. 使用型別限制gtltgelemax_length…)在參數層面提前攔截錯誤。

  4. 避免在模型層面使用可變物件作為預設值,改用 default_factory 或在路由內初始化。

  5. 測試所有組合:必填、缺省、錯誤型別,確保 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 valuerequired 的概念後,你就能在設計 API 時,既保留彈性又避免錯誤,讓前端開發者與使用者都有更好的體驗。祝你在 FastAPI 的開發旅程中,寫出更乾淨、更可靠的介面!