本文 AI 產出,尚未審核

FastAPI 課程 – 路由(Routing)

主題:查詢參數(Query Parameters)


簡介

RESTful API 中,除了路徑參數(Path Parameter)外,查詢參數(Query Parameters)是最常見的資料傳遞方式。它們被附加在 URL 後方(?key=value&...),用來過濾、分頁、排序或提供額外的選項。
FastAPI 以 型別提示 為核心,讓我們能夠在宣告函式簽名時直接定義查詢參數的型別、預設值與驗證規則,框架會自動完成解析、錯誤回應與 OpenAPI 文件生成。

掌握查詢參數的使用方式,不僅能寫出 易讀、可維護 的程式碼,還能提升 API 的使用體驗與安全性。以下內容將從概念說明、實作範例、常見陷阱到最佳實踐,完整呈現 FastAPI 中查詢參數的全貌,適合 初學者 迅速上手,也能讓 中階開發者 深入了解細節。


核心概念

1. 基本宣告 – 直接在函式參數中使用

FastAPI 會自動把 非路徑參數(即函式簽名中未以 {} 包住的參數)視為查詢參數。只要給予型別提示,框架即會從 request.query_params 取值。

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/")
def read_items(q: str):
    """
    只要在 URL 中帶入 ?q=關鍵字,即可取得對應的字串。
    例如:GET /items/?q=fastapi
    """
    return {"query": q}

重點q必填 查詢參數,若未提供會回傳 422 錯誤。


2. 預設值 – 讓參數變為可選

若在參數後面加上預設值,FastAPI 會把它視為 可選,且在未提供時使用預設值。

@app.get("/items/")
def read_items(q: str = None):
    """
    q 為可選參數,未提供時回傳 None。
    """
    return {"query": q}

提示:使用 Optional[str]from typing import Optional)可以讓型別更清晰。


3. 型別驗證與自動轉換

FastAPI 會根據型別提示自動將字串轉成目標型別,並在轉換失敗時回傳 422 錯誤。

@app.get("/users/")
def read_users(skip: int = 0, limit: int = 10):
    """
    - skip: 要跳過的筆數,預設 0
    - limit: 回傳的筆數上限,預設 10
    """
    return {"skip": skip, "limit": limit}

範例GET /users/?skip=5&limit=20{"skip":5,"limit":20}
skip=abc,則會得到驗證錯誤。


4. 列表參數 – 多值查詢

使用 list(或 Tuple)可以一次接收多個相同鍵名的值,FastAPI 會自動聚合成列表。

from typing import List

@app.get("/search/")
def search(tags: List[str] = []):
    """
    tags 可接受多個 ?tags=python&tags=fastapi 的寫法。
    """
    return {"tags": tags}

實例GET /search/?tags=python&tags=fastapi{"tags":["python","fastapi"]}


5. 別名(Alias)與描述(Description)

有時候前端或第三方服務使用的參數名稱不符合 Python 命名規則,我們可以透過 Query 物件設定別名、預設值、說明與驗證條件。

from fastapi import Query

@app.get("/products/")
def list_products(
    page: int = Query(1, ge=1, description="第幾頁,從 1 開始"),
    size: int = Query(20, le=100, description="每頁筆數,最大 100"),
    sort_by: str = Query(None, alias="sort", description="排序欄位")
):
    """
    - page: 頁碼,必須 >=1
    - size: 每頁筆數,最多 100
    - sort_by: 以別名 sort 提供,若未給則不排序
    """
    return {"page": page, "size": size, "sort_by": sort_by}

關鍵alias 讓 API 文檔顯示 sort,而程式內仍使用 sort_by


6. 依賴注入(Dependency Injection)與共用查詢參數

若多個路由需要相同的查詢參數(例如分頁資訊),可以建立 依賴,讓程式碼更 DRY。

from fastapi import Depends

def pagination_params(
    skip: int = Query(0, ge=0),
    limit: int = Query(10, ge=1, le=100)
):
    return {"skip": skip, "limit": limit}

@app.get("/orders/")
def list_orders(pagination: dict = Depends(pagination_params)):
    """
    透過 Depends 注入分頁參數。
    """
    return {"orders": [], **pagination}

7. 高階驗證 – 正則、Enum、Custom Validator

FastAPI 支援 pydantic 的所有驗證功能,讓查詢參數也能使用 EnumRegex 或自訂驗證。

import re
from enum import Enum
from pydantic import BaseModel, validator

class OrderStatus(str, Enum):
    pending = "pending"
    shipped = "shipped"
    delivered = "delivered"

class SearchParams(BaseModel):
    q: str
    email: str

    @validator("email")
    def email_must_be_valid(cls, v):
        if not re.fullmatch(r"[^@]+@[^@]+\.[^@]+", v):
            raise ValueError("Invalid email format")
        return v

@app.get("/orders/search/")
def search_orders(params: SearchParams = Depends()):
    """
    使用 Pydantic Model 作為查詢參數,支援複雜驗證。
    """
    return {"query": params.q, "email": params.email}

優點:所有驗證規則集中於 SearchParams,維護更方便。


常見陷阱與最佳實踐

陷阱 為什麼會發生 解決方式
未設定預設值導致參數變為必填 只寫 def foo(q: str):,若前端忘記傳參數會回 422 為非必要參數加 = None 或使用 Optional
使用可變預設值(list、dict) Python 預設值在函式定義時就被建立,會共享同一個物件 使用 Query([])field(default_factory=list)
過度依賴 request.query_params 手動解析會失去型別檢查與自動文件產生的好處 盡量使用函式參數或 Depends 注入
**忘記設定 alias**導致前端參數名稱不一致 API 要求 ?sort=price,卻寫成 sort_by 使用 Query(..., alias="sort")
未限制字串長度或數值範圍 可能收到惡意或不合理的請求,造成效能或安全問題 加入 ge, le, max_length 等限制
重複定義同名參數 同時在路徑與查詢中使用相同名稱,FastAPI 會優先路徑,可能產生混淆 保持命名一致或使用別名區分

最佳實踐

  1. 明確宣告必填與可選:使用 ...(Ellipsis)或 None 讓意圖清楚。
  2. 使用 Query 物件:一次設定預設值、別名、說明與驗證,提升自動文件品質。
  3. 集中驗證:若查詢參數較多,考慮使用 Pydantic Model 作為依賴,保持路由函式簡潔。
  4. 限制範圍:對於分頁、排序、搜尋字串長度等,設定合理的上下限,防止 DoS。
  5. 文件化:善用 descriptionexample,讓前端開發者一眼看懂使用方式。

實際應用場景

1. 商品搜尋與過濾

@app.get("/catalog/")
def catalog(
    q: str = Query(None, min_length=3, description="搜尋關鍵字"),
    categories: List[int] = Query([], alias="cat"),
    price_min: float = Query(0.0, ge=0),
    price_max: float = Query(10000.0, le=10000),
    sort: str = Query("relevance", regex="^(price|relevance|rating)$")
):
    """
    - 支援關鍵字、類別多選、價格區間與排序方式。
    - 前端可組合任意參數,後端只需處理已解析的 dict。
    """
    # 假設有一個 service 層負責實際查詢
    results = search_service.search(
        keyword=q,
        category_ids=categories,
        price_range=(price_min, price_max),
        order_by=sort,
    )
    return {"items": results}

2. 分頁 API(共用依賴)

def pagination(
    page: int = Query(1, ge=1),
    size: int = Query(20, ge=1, le=100)
):
    return {"skip": (page - 1) * size, "limit": size}

@app.get("/blog/posts/")
def list_posts(pagination: dict = Depends(pagination)):
    posts = blog_service.get_posts(skip=pagination["skip"], limit=pagination["limit"])
    return {"page": pagination, "posts": posts}

3. 多語系內容切換

@app.get("/articles/")
def get_articles(lang: str = Query("en", regex="^(en|zh|ja)$")):
    """
    根據 lang 參數回傳對應語言的文章。
    """
    return article_service.fetch_by_language(lang)

總結

查詢參數是 FastAPI 中最靈活、最常用的資料傳遞方式。透過 型別提示Query 物件與 Pydantic 驗證,我們可以:

  • 簡潔宣告 必填、可選、預設值與別名
  • 自動型別轉換與錯誤回應,減少手動檢查的負擔
  • 集中驗證(Enum、Regex、Custom Model)提升安全性
  • 共用依賴 讓分頁、過濾等功能維持 DRY 原則
  • 即時產生完整 OpenAPI 文件,讓前端與測試團隊快速上手

只要遵循 預設值、範圍限制、別名與文件化 的最佳實踐,便能寫出 易讀、可維護且具備良好使用者體驗 的 API。希望本篇文章能幫助你在實務開發中,快速且正確地運用 FastAPI 的查詢參數,打造高品質的服務。祝開發順利 🎉