這篇文章將教你如何在 FastAPI 中宣告與使用查詢參數 (Query Parameters),讓你能實現資料過濾、搜尋與分頁等功能。
什麼是查詢參數?
查詢參數是 URL 中位於問號 ? 之後的鍵值對 (Key-Value Pairs),通常用於對資料過濾、搜尋與分頁。就像在電商網站搜尋商品,「手機」是主要路徑,而「搜尋 samsung」、「只顯示有貨」就是透過查詢參數來控制的。
查詢參數基礎概念
1. 宣告查詢參數
在 FastAPI 中,只要函式參數沒有出現在路徑字串裡,預設就會被當成查詢參數處理。from fastapi import FastAPI
app = FastAPI()
inventory = [
{"name": "Keyboard"},
{"name": "Mouse"},
{"name": "Monitor"},
{"name": "Headset"},
]
# 範例 URL:http://127.0.0.1:8000/inventory?offset=0&limit=2
@app.get("/inventory")
def list_inventory(offset: int = 0, limit: int = 10):
return inventory[offset : offset + limit]
在此範例中,定義了 offset 和 limit 兩個整數型別的查詢參數,如果使用者未提供參數,就會自動使用 0 和 10 作為預設值。
2. 設定預設值
透過為參數指定一個值,你可以將其設為非必填,當使用者在請求 URL 中省略該參數,程式會自動使用你設定的預設值,這對於分頁功能特別實用。
from fastapi import FastAPI
app = FastAPI()
# URL 範例: http://127.0.0.1:8000/users/ 或 http://127.0.0.1:8000/users/?q=admin
@app.get("/users/")
async def read_users(q: str = "guest"):
# 如果 URL 沒有帶 ?q=...,q 的值就會是 "guest"
return {"user_type": q}
3. 選填參數
如果參數不是必填且沒有具體的預設值,可以把型別標註成 None,並給它預設值 None。
from fastapi import FastAPI
app = FastAPI()
# 範例 URL:http://127.0.0.1:8000/orders/123?note=urgent
@app.get("/orders/{order_id}")
def read_order(order_id: str, note: str | None = None):
data = {"order_id": order_id}
if note is not None:
data["note"] = note
return data
這裡的 note: str | None = None 表示 note 是一個可選的字串參數,沒有帶就會是 None,不會拋出錯誤。
4. 布林值轉換
FastAPI 在處理布林查詢參數時,會幫你把常見字串自動轉成 True / False。
from fastapi import FastAPI
app = FastAPI()
# 範例 URL:http://127.0.0.1:8000/articles/1?detailed=false
@app.get("/articles/{article_id}")
def read_article(article_id: int, detailed: bool = True):
article = {"article_id": article_id}
if detailed:
article["content"] = "這裡會放一大段文章內容..."
return article
當參數型別標註為 bool 時,像 true、1、on、yes 這類值會被轉成 True,而 false、0、off、no 則會被轉成 False。
實作範例
下面示範一個帶有「最低價格過濾」、「關鍵字搜尋」以及「分頁」的產品列表 API。
from fastapi import FastAPI
app = FastAPI()
# 模擬資料庫
fake_products = [
{"name": "iPhone 15", "price": 30000},
{"name": "MacBook Pro", "price": 60000},
{"name": "AirPods", "price": 5000},
{"name": "iPad Air", "price": 20000},
{"name": "Samsung S24", "price": 28000},
]
@app.get("/products/search")
def search_products(
offset: int = 0,
limit: int = 10,
min_price: int | None = None,
keyword: str | None = None,
):
"""
查詢產品列表,支援分頁、價格過濾與關鍵字搜尋
"""
# 複製一份資料以免影響原始數據
result = fake_products.copy()
# 1. 關鍵字搜尋(先進行篩選)
if keyword is not None:
keyword_lower = keyword.lower()
result = [p for p in result if keyword_lower in p["name"].lower()]
# 2. 價格過濾(繼續篩選)
if min_price is not None:
result = [p for p in result if p["price"] >= min_price]
# 計算符合條件的總數
total_count = len(result)
# 3. 分頁切片
result = result[offset : offset + limit]
return {"data": result, "count": total_count}
這個 /products/search 路徑做了幾件事:
1. keyword 也是選填條件,用來做名稱的模糊搜尋,沒有帶就不套用關鍵字過濾。
2. min_price 是選填條件,用來排除價格低於指定值的商品。
3. offset、limit 控制回傳資料的區間,預設會從第 0 筆開始取出最多 10 筆。
常見錯誤與解決方法
1. 必填參數未傳值
如果對查詢參數沒有設定預設值,FastAPI 會把它視為必填。請求時少了這個參數,就會回傳 422 驗證錯誤。
# ❌ 錯誤寫法:q 沒有預設值,變成必填
def read_items(q: str):
return {"q": q}
# ✅ 正確寫法:給定預設值 None (選填) 或具體值
def read_items(q: str | None = None):
return {"q": q}
2. 弄錯路徑參數與查詢參數
只要參數沒有定義在 @app.get("/path/{param}") 的路徑字串中,它就會自動變成查詢參數。
# ❌ 錯誤觀念:以為 user_id 是路徑參數
@app.get("/users/")
def get_user(user_id: int): # 這裡 user_id 其實變成了查詢參數 ?user_id=...
return {"user_id": user_id}
# ✅ 正確寫法:在裝飾器路徑中明確包含
@app.get("/users/{user_id}")
def get_user(user_id: int):
return {"user_id": user_id}
3. 選填參數放到必填參數前面
沒有預設值的參數(必填)必須放在有預設值的參數(選填)前面,否則程式會拋出 SyntaxError 無法執行。
# ❌ 錯誤寫法:選填參數 (有預設值) 放在 必填參數 (沒預設值) 前面
@app.get("/items/")
def read_items(q: str | None = None, item_id: int):
return {"item_id": item_id, "q": q}
# ✅ 正確寫法:必填 (item_id) 在前,選填 (q) 在後
@app.get("/items/")
def read_items(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
結語
查詢參數是靈活構建 API 的方式,能有效處理資料的篩選與呈現方式。請善用型別提示 (int, str, float) 來確保資料正確性,並正確區分「必填」與「選填」的預設值邏輯。
















