FastAPI Request 查詢參數驗證 (Query parameters validation)

在前面的章節中,我們學會了如何宣告簡單的查詢參數。但有時候我們需要更嚴格的限制,例如:參數必須是 50 個字元以內,或者必須符合特定的 Email 格式。

FastAPI 提供了 Query 類別來幫助我們達成這些需求。

使用 Query 增加額外驗證

首先,你需要從 fastapi 導入 Query

from typing import Optional
from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
    if q:
        results.update({"q": q})
    return results

使用 Annotated (推薦)

在現代 FastAPI 中,建議使用 Annotated 搭配 Query 類別:

from typing import Annotated, Optional
from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(
    q: Annotated[Optional[str], Query(max_length=50)] = None
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

這段程式碼的意思是:

  1. q 是一個選填的字串 (Optional[str])。
  2. 預設值為 None (由 Query(None, ...) 的第一個參數決定)。
  3. max_length=50:限制最大長度為 50 個字元。

如果使用者傳送的 q 超過 50 個字元,FastAPI 會自動回傳 422 錯誤。

其他驗證參數

除了 max_lengthQuery 還支援許多參數:

  • min_length:最小長度。
  • regex:使用正規表達式 (Regular Expression) 驗證。

例如,限制 q 必須是長度 3 到 50,且只能包含固定查詢字串:

@app.get("/items/")
async def read_items(
    q: Optional[str] = Query(None, min_length=3, max_length=50, regex="^fixedquery$")
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

必填欄位 (Required)

當你使用 Query 時,如何宣告必填?

使用 ...

在 Python 中,你不能直接寫 Query(max_length=50) 而不給第一個與預設值相關的參數。 FastAPI 支援使用 Ellipsis (...) 來明確表示「此欄位為必填」。

@app.get("/items/")
async def read_items(q: str = Query(..., min_length=3)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

如果請求中沒有 q,FastAPI 會報錯。

參數列表 (List) / 多個值

如果你想要接收一個參數列表,例如 ?q=foo&q=bar,可以明確定義型別為 List[str]

from typing import List

@app.get("/items/")
async def read_items(q: Optional[List[str]] = Query(None)):
    query_items = {"q": q}
    return query_items

請求 URL:http://localhost:8000/items/?q=foo&q=bar

回應 Body:

{
  "q": ["foo", "bar"]
}

你也可以替列表設定預設值:

@app.get("/items/")
async def read_items(q: List[str] = Query(["foo", "bar"])):
    query_items = {"q": q}
    return query_items

添加元數據 (Metadata)

你可以利用 title, description 等參數,為 Swagger UI 文件添加更多說明資訊。

@app.get("/items/")
async def read_items(
    q: Optional[str] = Query(
        None,
        title="Query string",
        description="應用於搜尋項目的查詢字串,限制長度為 3 單位以上。",
        min_length=3
    )
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

別名 (Alias)

假設你想讓 URL 參數名稱是 item-query (帶有連字線),但在 Python 中變數不能有連字線。你可以使用 alias

@app.get("/items/")
async def read_items(q: Optional[str] = Query(None, alias="item-query")):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

請求 URL:http://127.0.0.1:8000/items/?item-query=foobar (注意參數名是 item-query)。 但在 Python 程式碼中,你依然使用 q 來存取它。

棄用參數 (Deprecated)

如果你想保留舊的參數但不希望新使用者使用,可以標記為 deprecated=True

@app.get("/items/")
async def read_items(
    q: Optional[str] = Query(
        None,
        alias="item-query",
        title="Query string",
        description="Query string for the items to search in the database that have a good match",
        min_length=3,
        max_length=50,
        regex="^fixedquery$",
        deprecated=True,
    )
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

在 Swagger UI 中,這個參數會被畫上一條刪除線,提示使用者該參數已棄用。

總結

  • 使用 Query 可以對查詢參數進行更細緻的控管。
  • 常見驗證:max_length, min_length, pattern (取代舊有的 regex)。
  • 必填參數使用 ...
  • 支援接收列表參數 (List[str])。
  • alias 用於處理不符合 Python 變數命名規則的 URL 參數。
  • 記得善用 titledescription 來豐富你的 API 文件。