本文 AI 產出,尚未審核

FastAPI – 資料驗證與轉換(Validation & Serialization)

主題:Enum 型別自動轉換


簡介

在 Web API 開發中,列舉(Enum) 常被用來限制客戶端只能傳入特定的值,例如訂單狀態、使用者角色或產品類別。若每次都手動檢查字串是否合法,程式碼會變得冗長且易出錯。
FastAPI 內建對 Python Enum 的支援,能在 請求驗證 時自動把字串、整數或其他可比較的值轉換成對應的 Enum 成員,並在回傳時自動序列化為友好的字串(或值)。這不只提升開發效率,也讓 API 規格更清晰、錯誤回報更一致。

本文將說明 Enum 型別在 FastAPI 中的自動轉換機制,並提供實作範例、常見陷阱與最佳實踐,幫助你在實務專案中正確運用。


核心概念

1️⃣ Python Enum 基礎

from enum import Enum, IntEnum

class OrderStatus(str, Enum):
    """訂單狀態的列舉,繼承自 str 讓 JSON 序列化更直觀"""
    PENDING = "pending"
    PAID    = "paid"
    SHIPPED = "shipped"
    CANCELED = "canceled"

class UserRole(IntEnum):
    """使用者角色的列舉,繼承自 IntEnum 讓值為整數"""
    ADMIN = 1
    MODERATOR = 2
    USER = 3
  • strint 為基底的 Enum,在 JSON 序列化 時會直接輸出基底值,避免出現 OrderStatus.PENDING 這樣的物件表示。
  • Enum 本身是不可變的、具備唯一性,適合作為 資料驗證 的「白名單」。

2️⃣ FastAPI 如何自動轉換

當你在 Pydantic 模型或 路由參數 中宣告 Enum 類別時,FastAPI 會:

  1. 解析請求:把傳入的字串(或整數)與 Enum 成員比對。
  2. 驗證成功:回傳對應的 Enum 成員實例。
  3. 驗證失敗:拋出 422 Unprocessable Entity,回應中會列出「值不在允許的列舉」訊息。
  4. 回傳序列化:Response 內的 Enum 會自動轉成基底值(str/int),符合 OpenAPI 規範。

3️⃣ 路由參數的 Enum

from fastapi import FastAPI, Path

app = FastAPI()

@app.get("/orders/{status}")
async def list_orders(status: OrderStatus = Path(..., description="訂單狀態")):
    """
    依照訂單狀態過濾清單。
    - 若 `status` 為不合法值,FastAPI 會自動回傳 422。
    """
    # `status` 已是 OrderStatus 成員,可直接使用其屬性
    return {"filter": status, "message": f"取得 {status.value} 訂單"}
  • URL 路徑查詢參數表單欄位 都可以直接使用 Enum。
  • Path(..., description=...) 讓 OpenAPI 文件自動顯示可接受的列舉值。

4️⃣ 請求 Body 中的 Enum

from pydantic import BaseModel

class CreateUserRequest(BaseModel):
    username: str
    role: UserRole  # 直接使用 Enum

@app.post("/users")
async def create_user(payload: CreateUserRequest):
    """
    建立新使用者。`role` 若傳入非 1/2/3 會回傳 422。
    """
    # payload.role 為 UserRole 成員,可直接比較
    if payload.role == UserRole.ADMIN:
        # 執行管理員專屬邏輯
        pass
    return {"username": payload.username, "role": payload.role}
  • Pydantic 會在模型解析階段完成 Enum 轉換。
  • 回傳時,payload.role 會序列化為整數 1、2、3

5️⃣ 自訂 Enum 的序列化方式

若想要在回傳 JSON 時使用 自訂字串(例如顯示中文),可以覆寫 __str__ 或使用 Pydantic 的 json_encoders

class PaymentMethod(str, Enum):
    CREDIT = "credit"
    PAYPAL = "paypal"
    ATM    = "atm"

    def __str__(self) -> str:
        # 讓 API 回傳更友好的中文描述
        mapping = {
            "credit": "信用卡",
            "paypal": "PayPal",
            "atm": "ATM 轉帳"
        }
        return mapping[self.value]

from pydantic import BaseModel

class PaymentInfo(BaseModel):
    method: PaymentMethod

    class Config:
        json_encoders = {
            PaymentMethod: lambda v: str(v)   # 使用上方 __str__ 的結果
        }
  • 只要在 Config.json_encoders 中指定,就能讓 所有回傳 皆使用自訂的文字。

常見陷阱與最佳實踐

陷阱 說明 解決方案
忘記繼承基底類別 class MyEnum(Enum): 只繼承 Enum,回傳時會是 MyEnum.VALUE,不符合 JSON 預期。 繼承 strint(如 class Status(str, Enum):
大小寫不一致 客戶端傳入 Pending 而 Enum 為 pending,驗證失敗。 使用 case_insensitive=True(Pydantic 1.10+)或自行實作前置處理。
重複值 多個成員使用相同值會導致只保留第一個,其他成員不可直接比對。 確保每個成員的值唯一,或使用 @unique 裝飾器檢查。
Enum 與資料庫不相容 ORM(如 SQLAlchemy)需要把 Enum 轉成資料庫支援的類型。 在模型層使用 Enum,在 ORM 層使用 EnumString 欄位,並在 CRUD 時自行轉換。
自訂序列化忘記註冊 覆寫 __str__ 後未在 json_encoders 設定,回傳仍是原始值。 設定 Config.json_encoders,或使用 FastAPIresponse_model_exclude_unset

最佳實踐

  1. 始終繼承基底類別str/int),確保 OpenAPI 文件正確顯示。
  2. 使用 Enum 作為單一真相來源:所有驗證、業務邏輯與文件皆以同一個 Enum 定義為準。
  3. 在 Pydantic Config 中集中管理序列化,避免每個路由重複編寫。
  4. 加入 descriptionexample,讓 Swagger UI 更友善。
  5. 測試列舉的邊界情況(大小寫、空字串、非法值),確保 422 回應符合預期。

實際應用場景

場景 為何使用 Enum 範例程式碼
訂單系統:過濾、更新訂單狀態 防止非法狀態寫入資料庫 GET /orders/{status}PATCH /orders/{order_id}
權限管理:角色授權檢查 角色固定且需對應數字 ID POST /users 中的 role: UserRole
支付平台:付款方式限定 不同方式需要不同的後端流程 PaymentInfo 模型與自訂中文回傳
多語系 API:回傳語言代碼 語言代碼必須是已註冊的 ISO 639‑1 GET /content?lang=LanguageCode
IoT 裝置:設備類型或指令代碼 裝置只能接受預定義指令 PUT /devices/{device_id}/command

示例:在訂單系統中,若前端傳入 status=invalid,FastAPI 會回傳:

{
  "detail": [
    {
      "loc": ["path", "status"],
      "msg": "value is not a valid enumeration member; permitted: 'pending', 'paid', 'shipped', 'canceled'",
      "type": "type_error.enum"
    }
  ]
}

這樣的錯誤訊息即時告知開發者哪裡出錯,降低除錯成本。


總結

  • Enum 為 API 提供 明確、可驗證的值集合,FastAPI 透過 Pydantic 完成 自動轉換與序列化,讓開發者只需專注於業務邏輯。
  • 正確的 繼承基底類別統一定義、以及 在 Config 中設定序列化,是避免常見陷阱的關鍵。
  • 在實務中,將 列舉作為單一真相來源,不僅提升程式碼可讀性,也讓 OpenAPI 文件自動保持同步,減少文件與實作不一致的風險。

掌握了 Enum 的自動轉換,你的 FastAPI 專案將更安全、更易維護,也能提供更友善的 API 使用者體驗。祝開發順利! 🚀