【FastAPI 學習筆記 EP.05】進階驗證與 Metadata

更新 發佈閱讀 10 分鐘

這篇文章教你如何使用 FastAPI 的 QueryPath 函式,對 API 的輸入參數進行嚴格的格式驗證,並且為 API 文件添加描述資訊。

什麼是進階驗證?

進階驗證 (Query & Path Validation) 是指在接收 HTTP 請求時,對 URL 中的查詢參數 (Query Parameters) 與路徑參數 (Path Parameters) 設定更嚴格的規則。不符合規定的資料(例如長度不足、數值範圍錯誤)會直接被擋下,並且回傳 HTTP 422 錯誤。

Metadata 則是用於描述這些參數的額外資訊(例如:參數的標題、描述、範例),這些資訊不影響程式運作,但會直接顯示在 Swagger UI 文件上,讓前端開發者更容易看懂。

Query、Path 基礎概念

1. 查詢參數驗證 (Query Parameters)

使用 Query 類別來限制 URL 中 ? 之後的查詢參數 (Query String),常用的驗證參數包括 min_length(最小長度)、max_length(最大長度)和 pattern(正規表達式)。

from fast
api import FastAPI, Query
from typing import Annotated

app = FastAPI()

@app.get("/items/")
# 驗證 keyword:選填,只能包含英文字母,長度需介於 3 到 50 之間
def read_items(
    keyword: Annotated[
        str | None,
        Query(min_length=3, max_length=50, pattern="^[a-zA-Z]+$")
    ] = None
):
    return {"keyword": keyword}

在此範例中,限制 keyword 從頭到尾僅能包含英文字母。如果使用者輸入 ?keyword=123,就會觸發驗證錯誤。

2. 路徑參數驗證 (Path Parameters)

使用 Path 類別來限制 URL 路徑中的變數,常用參數為 gt (大於)、ge (大於等於)、lt (小於)、le (小於等於)。

from fastapi import FastAPI, Path
from typing import Annotated

app = FastAPI()

@app.get("/items/{item_id}")
# 驗證 item_id:必須是大於等於 1 的整數,並添加文件標題
def read_item(
    item_id: Annotated[int, Path(title="The ID of the item", ge=1)]
):
    return {"item_id": item_id}

在此範例中,Path 確保了 ID 不會是負數或 0。

3. 添加 Metadata

為了讓 API 文件更友善,我們可以在 QueryPath 中加入 title(參數標題)、description(詳細說明)以及 example(範例值)。

from fastapi import FastAPI, Query
from typing import Annotated

app = FastAPI()

@app.get("/search/")
def search(
    keyword: Annotated[
        str | None,
        Query(
            title="搜尋關鍵字",
            description="請輸入產品名稱,支援模糊搜尋",
            example="iPhone 15"  # 在文件上顯示的預設範例值
        )
    ] = None
):
    return {"keyword": keyword}

實作範例

以下範例展示一個「進入某個商品分類(如:3C 家電、服飾)」後,再進行關鍵字搜尋的功能。

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

app = FastAPI()

@app.get("/categories/{category_id}/products")
def search_products_by_category(
    # 路徑參數:鎖定分類 ID
    category_id: Annotated[
        int,
        Path(
            title="分類 ID",
            description="商品分類的唯一識別碼 (1: 3C, 2: 服飾, 3: 食品...)",
            ge=1,   # ID 必須大於等於 1
            le=100, # 假設系統目前只有 100 個分類
            example=1  # 文件上的範例 ID
        )
    ],
    # 查詢參數:分類內的關鍵字搜尋
    keyword: Annotated[
        str | None,
        Query(
            title="搜尋關鍵字",
            description="搜尋該分類內的產品名稱",
            min_length=2,
            max_length=30,
            example="無線耳機"  # 文件上的輸入框預設值
        )
    ] = None,
    # 查詢參數:分頁限制
    limit: Annotated[
        int,
        Query(
            title="每頁筆數",
            description="限制回傳的資料筆數",
            ge=1,
            le=50,
            example=20
        )
    ] = 10
):

    """
    獲取指定「分類」下的產品列表,並支援關鍵字過濾
    """
    result = {
        "category_id": category_id,
        "result_limit": limit,
        "items": []
    }

    if keyword:
        result.update({
            "filter_by": keyword,
            "message": f"正在搜尋分類ID({category_id})中包含'{keyword}'名稱的商品"
        })
    else:
        result.update({
            "message": f"列出分類ID({category_id})中的所有商品"
        })

    return result

常見錯誤與解決方法

1. 搞混「必填欄位」與「預設值」的設定

在宣告 Path 參數時,賦予預設值 None,但路徑參數在 URL 結構中本質上就是「必填」的,邏輯上會產生衝突。

from fastapi import FastAPI, Path
from typing import Annotated

# 錯誤寫法:Path 是必填的,不該有預設值 None
# def bad_example(item_id: Annotated[int, Path(title="ID")] = None):
#     pass

# 正確寫法:路徑參數本身就是必填,不需要賦予預設值
def good_example(item_id: Annotated[int, Path(title="ID")]):
    pass

2. 驗證參數與資料型態不匹配

這是使用進階驗證時最容易發生的崩潰錯誤。我們必須清楚區分「字串驗證參數」與「數值驗證參數」,不能混用。

# 錯誤範例 1:對「整數」使用「長度」驗證

# Python 的 int 沒有 len(),這會導致程式錯誤

# item_id: Annotated[int, Path(min_length=3)]


# 錯誤範例 2:對「字串」使用「大小」驗證

# 字串雖然可以比大小,但在 API 邏輯中通常不合理

# keyword: Annotated[str, Query(ge=5)]


# 正確觀念:

# Query 跟 Path 都能使用字串跟數值驗證參數

# int, float 用 ge, le, gt, lt

# str 用 min_length, max_length

結語

掌握 QueryPath 的驗證功能,你就能控制 API 的輸入規則,為後端阻擋錯誤數據。同時,妥善運用 titledescriptionexample 等 Metadata,能讓你的 API 文件具備良好的可讀性,降低前後端的溝通成本。

留言
avatar-img
留言分享你的想法!
avatar-img
梧笙 WuSheng 的沙龍
65會員
6內容數
梧笙,取意吾生,在網路世界留下我的痕跡。
2025/12/07
這篇文章將教你如何在 FastAPI 中定義與接收請求主體 (Request Body),當客戶端需要傳送 JSON 格式的詳細資料給伺服器(例如註冊表單或新增商品)時,就必須使用請求主體 (Request Body)。
Thumbnail
2025/12/07
這篇文章將教你如何在 FastAPI 中定義與接收請求主體 (Request Body),當客戶端需要傳送 JSON 格式的詳細資料給伺服器(例如註冊表單或新增商品)時,就必須使用請求主體 (Request Body)。
Thumbnail
2025/12/05
這篇文章直接教你如何在 FastAPI 中宣告與使用查詢參數 (Query Parameters),讓你能實現資料過濾、搜尋與分頁等功能。
Thumbnail
2025/12/05
這篇文章直接教你如何在 FastAPI 中宣告與使用查詢參數 (Query Parameters),讓你能實現資料過濾、搜尋與分頁等功能。
Thumbnail
2025/12/03
這篇文章將會教你如何在 FastAPI 中宣告並讀取路徑參數 (Path Parameters),你將學會如何透過 URL 傳遞變數給伺服器,實現取得特定用戶資料或商品資訊等功能。
Thumbnail
2025/12/03
這篇文章將會教你如何在 FastAPI 中宣告並讀取路徑參數 (Path Parameters),你將學會如何透過 URL 傳遞變數給伺服器,實現取得特定用戶資料或商品資訊等功能。
Thumbnail
看更多
你可能也想看
Thumbnail
我們在「【🔒 Python API框架篇 - FastAPI】Ep.1 啟航」稍微帶大家認識了FastAPI這個框架, 它讓我們快速的架設一個API服務, 並提供了許多標準化功能, 只要照著規範走就能快速的開發出來, 但開發出來之後, 我們會希望開放給一般使用者使用, 而一般使用者較能夠操作的媒介
Thumbnail
我們在「【🔒 Python API框架篇 - FastAPI】Ep.1 啟航」稍微帶大家認識了FastAPI這個框架, 它讓我們快速的架設一個API服務, 並提供了許多標準化功能, 只要照著規範走就能快速的開發出來, 但開發出來之後, 我們會希望開放給一般使用者使用, 而一般使用者較能夠操作的媒介
Thumbnail
我們在「【🔒 Python API框架篇 - FastAPI】Ep.1 啟航」稍微帶大家認識了FastAPI這個框架, 它讓我們快速的架設一個API服務, 並提供了許多標準化功能, 只要照著規範走就能快速的開發出來, 但我們除了能開發出應用之外, 也要設計的更人性化一點, API最重要的就是路由了
Thumbnail
我們在「【🔒 Python API框架篇 - FastAPI】Ep.1 啟航」稍微帶大家認識了FastAPI這個框架, 它讓我們快速的架設一個API服務, 並提供了許多標準化功能, 只要照著規範走就能快速的開發出來, 但我們除了能開發出應用之外, 也要設計的更人性化一點, API最重要的就是路由了
Thumbnail
API是我們與其他系統介接的標準化規格, 那一份好的規格勢必要能夠達到引導與驗證的作用, 避免對方介接錯誤, 引發後續的災難性損失, 因此這一章節就是要教我們如何定義每個API的欄位怎麼填? 資料型態是什麼? 以及如何生成API文件。 我們在「【🔒 Python API框架篇 - Fas
Thumbnail
API是我們與其他系統介接的標準化規格, 那一份好的規格勢必要能夠達到引導與驗證的作用, 避免對方介接錯誤, 引發後續的災難性損失, 因此這一章節就是要教我們如何定義每個API的欄位怎麼填? 資料型態是什麼? 以及如何生成API文件。 我們在「【🔒 Python API框架篇 - Fas
Thumbnail
要如何使用unicorn啟動多個FastAPI服務, 歡迎參考我們的「【💊 Python的解憂錦囊 - FastAPI】如何啟動多個Workers」。 當我們試著設計帶入模組化時… 我們在「【💊 Python的解憂錦囊 - FastAPI】使用 lifespan 來共享資料與管理生命週期
Thumbnail
要如何使用unicorn啟動多個FastAPI服務, 歡迎參考我們的「【💊 Python的解憂錦囊 - FastAPI】如何啟動多個Workers」。 當我們試著設計帶入模組化時… 我們在「【💊 Python的解憂錦囊 - FastAPI】使用 lifespan 來共享資料與管理生命週期
Thumbnail
我們在「【🔒 Python API框架篇 - FastAPI】Ep.1 啟航」有說明如何使用uvicorn來啟動FastAPI服務, 假設今天我們的API是一個CPU密集型的運算服務時, 通常我們會希望開啟多個行程來幫忙處理, 那麼大致上的撰寫方式會像這樣: app = FastAPI( ti
Thumbnail
我們在「【🔒 Python API框架篇 - FastAPI】Ep.1 啟航」有說明如何使用uvicorn來啟動FastAPI服務, 假設今天我們的API是一個CPU密集型的運算服務時, 通常我們會希望開啟多個行程來幫忙處理, 那麼大致上的撰寫方式會像這樣: app = FastAPI( ti
Thumbnail
我們在「【🔒 Python API框架篇 - FastAPI】Ep.1 啟航」有分享 FastAPI 這套API框架, 那麼當我們想要在應用程式剛執行時就註冊一些事件或者共享GPU運算模型、變數…等,當整個應用程式關閉時也進行釋放作業, 這樣的一個週期循環就是所謂的生命週期, 而在FastAPI這
Thumbnail
我們在「【🔒 Python API框架篇 - FastAPI】Ep.1 啟航」有分享 FastAPI 這套API框架, 那麼當我們想要在應用程式剛執行時就註冊一些事件或者共享GPU運算模型、變數…等,當整個應用程式關閉時也進行釋放作業, 這樣的一個週期循環就是所謂的生命週期, 而在FastAPI這
Thumbnail
當我們在開發一個AI應用服務時, 常常會需要載入大模型, But… 我們總不可能每一次的請求就載入一次模型吧! 這樣太沒有效率了, 也非常的浪費資源, 因此我們通常會希望應用程式啟動時就能夠載入模型, 之後每一次的請求只要讓模型進行運算即可, 那麼在FastAPI的框架中究竟要如何使用呢? 首
Thumbnail
當我們在開發一個AI應用服務時, 常常會需要載入大模型, But… 我們總不可能每一次的請求就載入一次模型吧! 這樣太沒有效率了, 也非常的浪費資源, 因此我們通常會希望應用程式啟動時就能夠載入模型, 之後每一次的請求只要讓模型進行運算即可, 那麼在FastAPI的框架中究竟要如何使用呢? 首
Thumbnail
關於FastAPI這個框架為什麼有什麼樣的優勢, 為什麼會這麼熱門? 歡迎參考「【Python 技術選型】如何選出適合的API框架呢?」。 站在巨人的肩膀上 FastAPI主要基於以下兩個重要的元件組成, Starlette與Pydantic, 就讓我們來看看兩者的關係吧! 安裝 pip
Thumbnail
關於FastAPI這個框架為什麼有什麼樣的優勢, 為什麼會這麼熱門? 歡迎參考「【Python 技術選型】如何選出適合的API框架呢?」。 站在巨人的肩膀上 FastAPI主要基於以下兩個重要的元件組成, Starlette與Pydantic, 就讓我們來看看兩者的關係吧! 安裝 pip
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News