本文 AI 產出,尚未審核

FastAPI – 多值查詢參數(List[str])教學

簡介

在 RESTful API 中,查詢參數(Query Parameter)是最常見的過濾或搜尋方式。當使用者需要一次傳遞多個相同名稱的值(例如 ?tag=python&tag=fastapi),就會出現「多值查詢參數」的需求。FastAPI 內建對 listsettuple 等可迭代型別的支援,讓開發者只要在函式簽名上宣告 List[str],框架就會自動把 URL 中的多個值轉換成 Python 的列表。

正確掌握 多值查詢參數 的寫法,能讓 API:

  • 更彈性:一次接受多個條件,不必為每個條件寫獨立的端點。
  • 更易維護:統一的參數解析邏輯,減少重複程式碼。
  • 提升使用者體驗:前端或第三方服務只要組合 URL 即可完成複雜篩選。

本篇文章將從概念說明、實作範例、常見陷阱與最佳實踐,帶你一步步熟悉 FastAPI 中的 List[str] 查詢參數,並探討實務應用情境。


核心概念

1. 為什麼使用 List[str] 而不是單一 str

單一字串只能傳遞一個值,若要同時搜尋多個關鍵字或標籤,就必須自行在程式裡切割字串(例如 "python,fastapi")。這樣的做法:

  • 不符合 HTTP 標準?tag=python,fastapi 把逗號視為字串的一部份,與 ?tag=python&tag=fastapi 的語意不同。
  • 可讀性差:使用者必須記得自行編碼分隔符號。

FastAPI 直接支援 同名多值,只要把參數型別寫成 List[str],框架會自動收集所有同名的 query string。

2. 基本語法

from typing import List
from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(tags: List[str] = Query(None)):
    """
    `tags` 會自動變成 List[str],若 URL 為
    /items/?tags=python&tags=fastapi
    則 tags == ["python", "fastapi"]
    """
    return {"tags": tags}
  • Query(None) 表示參數是可選的,若不傳遞則為 None
  • 若想讓參數必填,改成 Query(...)

3. 預設值與驗證

FastAPI 允許在 Query 中設定預設值、最小/最大長度、正則表達式等驗證規則:

@app.get("/search/")
async def search(
    keywords: List[str] = Query(
        default=["fastapi"], 
        min_length=1, 
        max_length=20,
        description="搜尋關鍵字,支援多值"
    )
):
    return {"keywords": keywords}
  • 若使用者未提供 keywords,會自動使用 ["fastapi"] 作為預設。
  • 每個字串長度會受到 min_lengthmax_length 的限制,超出會回傳 422 錯誤。

4. 與 Path、Body 參數的混用

查詢參數可以與路徑參數 (Path) 以及請求主體 (Body) 同時存在,只要在函式簽名中分別標註即可:

from fastapi import Path, Body

@app.post("/users/{user_id}/items/")
async def add_items(
    user_id: int = Path(..., description="使用者 ID"),
    tags: List[str] = Query(..., description="項目標籤"),
    payload: dict = Body(..., description="其他資料")
):
    # 這裡的 tags 仍是 List[str]
    return {"user_id": user_id, "tags": tags, "payload": payload}

程式碼範例

以下提供 5 個實用範例,涵蓋從最簡單到進階的使用情境,並附上詳細註解說明。

範例 1️⃣:最簡單的多值查詢參數

# file: app.py
from typing import List
from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/books/")
async def list_books(categories: List[str] = Query(None)):
    """
    URL 範例:
    /books/?categories=fiction&categories=history
    -> categories == ["fiction", "history"]
    """
    # 直接回傳收到的列表,實務上通常會放到資料庫查詢條件中
    return {"categories": categories}

重點:只要在 Query 中使用 List[str],FastAPI 會自動把同名的 query string 合併成列表。


範例 2️⃣:設定必填與驗證規則

@app.get("/events/")
async def filter_events(
    tags: List[str] = Query(
        ...,                     # 必填
        min_length=2,            # 每個字串最少 2 個字元
        max_length=15,           # 每個字串最多 15 個字元
        description="活動標籤"
    )
):
    """
    URL 範例:
    /events/?tags=music&tags=live
    若任一 tag 長度不符合規則,會回傳 422 錯誤。
    """
    return {"tags": tags}

技巧:使用 ... 讓參數變為必填,搭配 min_lengthmax_length 防止惡意或錯誤輸入。


範例 3️⃣:預設值 + 列舉型別 (Enum)

如果想限制只能傳入特定字串,可結合 Enum

from enum import Enum

class Color(str, Enum):
    red = "red"
    green = "green"
    blue = "blue"

@app.get("/products/")
async def get_products(
    colors: List[Color] = Query(
        default=[Color.red],   # 預設只返回 red
        description="可接受的顏色清單"
    )
):
    # FastAPI 會自動把字串轉成 Enum 成員
    return {"colors": [c.value for c in colors]}

說明:若使用者提供 ?colors=green&colors=yellowyellow 不是 Enum 成員,會回傳 422。


範例 4️⃣:結合 Path、Query、Body

這是一個真實的「新增商品」API,示範多值查詢參數與其他參數的混用:

from fastapi import Path, Body

class ItemCreate(BaseModel):
    name: str
    price: float
    description: str | None = None

@app.post("/stores/{store_id}/items/")
async def create_item(
    store_id: int = Path(..., description="店鋪編號"),
    tags: List[str] = Query([], description="商品標籤,可多值"),
    item: ItemCreate = Body(..., description="商品詳細資訊")
):
    """
    - `store_id` 來自路徑
    - `tags` 來自查詢參數,可為空列表
    - `item` 來自請求體 (JSON)
    """
    # 假設將資料寫入資料庫
    saved = {
        "store_id": store_id,
        "tags": tags,
        "item": item.dict(),
    }
    return {"message": "Item created", "data": saved}

要點tags 若未提供,會得到空列表 [],方便直接在資料庫查詢時使用 IN 條件。


範例 5️⃣:自訂分隔符號(逗號分割)

有時前端不方便一次傳多個同名參數,會改用逗號分割的單一字串。可以自行寫一個依賴函式將其轉成 List[str]

from fastapi import Depends

def csv_to_list(q: str | None = Query(None)) -> List[str]:
    """
    把 ?keywords=python,fastapi,async 轉成 ["python","fastapi","async"]
    若 q 為 None,回傳空列表
    """
    return q.split(",") if q else []

@app.get("/articles/")
async def search_articles(keywords: List[str] = Depends(csv_to_list)):
    return {"keywords": keywords}

說明:使用 Depends 可以把自訂的前置處理邏輯抽離,使路由函式保持乾淨。


常見陷阱與最佳實踐

陷阱 說明 解決方式
忘記給預設值 List[str] 若未指定預設值,FastAPI 會把缺省的參數視為 None,導致後續需要 if tags is None 的判斷。 使用 Query([])Query(None, ...),根據需求決定是否接受 None
重複名稱衝突 同一個路由同時使用 PathQueryBody,若名稱相同會產生衝突。 為每個來源使用不同的變數名稱,或使用 alias 參數改變外部名稱(如 Query(..., alias="tag"))。
未驗證字串長度 大量或過長的字串會佔用過多資源,甚至造成 DoS。 加上 max_lengthregex 或自訂驗證函式。
逗號分割的單一參數 前端傳 ?tags=python,fastapi 時,FastAPI 只會把整個字串視為單一元素。 使用 Depends 或自訂 Query(..., convert_underscores=False) 之類的依賴處理。
Enum 轉換失敗 傳入不屬於 Enum 的值會直接回 422,訊息可能不夠友好。 自訂 HTTPException,或在 response_model 中提供更明確的錯誤說明。

最佳實踐

  1. 明確文件化:在 QueryPathBody 中使用 descriptionexample,讓自動產生的 OpenAPI 文檔清晰可讀。
  2. 使用型別提示List[str]Set[int]Tuple[float, ...] 等型別不僅提升 IDE 補全,也讓 FastAPI 自動生成驗證。
  3. 預設空集合:若業務上允許「不傳」即視為「無條件」搜尋,建議使用 Query([]),避免在程式中處理 None
  4. 分層驗證:先在 Query 中做基礎長度/正則驗證,再在業務層(service)中做更複雜的邏輯檢查。
  5. 測試多值情境:使用 TestClient 撰寫單元測試,確保 ?tag=a&tag=b?tag=a,b(自訂處理)都能得到預期結果。

實際應用場景

場景 需求 多值查詢參數的角色
商品搜尋 使用者可同時勾選多個類別、品牌、標籤 categories=electronics&categories=homebrands=sony&brands=panasonic
日誌檢索 管理員想一次查看多個服務的錯誤日誌 services=auth&services=payment
社交平台 文章或貼文支援標籤過濾 tags=python&tags=fastapi
統計報表 從多個地區、時間區間取得資料 regions=us&regions=eu&months=2024-01&months=2024-02
API 授權 客戶端可以一次傳入多個 API 金鑰以驗證不同資源 api_keys=key1&api_keys=key2

實務建議:在資料庫查詢時,將收到的 List[str] 直接映射為 IN 條件(SQLAlchemy、Tortoise‑ORM 等),可有效降低程式碼複雜度。例如

stmt = select(Item).where(Item.category.in_(categories))

總結

  • 多值查詢參數 是 API 設計中常見且重要的需求,FastAPI 只要在路由函式的參數上使用 List[str](或其他可迭代型別),就能自動將同名的 query string 合併為 Python 列表。
  • 透過 Query 可以設定 預設值、必填、長度驗證、Enum 限制,讓 API 更安全、文件更完整。
  • PathBody 同時使用時,只要保持變數名稱唯一或使用 alias,就不會產生衝突。
  • 常見陷阱包括忘記預設值、未驗證長度、逗號分割的單一參數等,遵循 最佳實踐(文件化、型別提示、空集合預設、分層驗證、測試)即可避免。
  • 在實務上,從商品搜尋、日誌檢索到統計報表等場景,都能藉由 List[str] 讓前端一次傳遞多個條件,後端僅需把列表直接套用於資料庫的 IN 查詢,既簡潔又高效。

掌握了 多值查詢參數 的寫法與注意事項後,你的 FastAPI 服務將變得更具彈性、更易維護,也能更快速地滿足日益複雜的業務需求。祝開發順利,Happy Coding! 🚀