本文 AI 產出,尚未審核

FastAPI

單元:路由(Routing)

主題:支援的 HTTP 方法(GET, POST, PUT, PATCH, DELETE, OPTIONS)


簡介

在 Web API 開發中,**路由(Routing)**是把客戶端的請求對應到程式碼的關鍵機制。FastAPI 以 ASGI 為底層,提供了直覺且型別安全的路由宣告方式,讓開發者可以輕鬆定義支援的 HTTP 方法(GET、POST、PUT、PATCH、DELETE、OPTIONS)以及相關的參數驗證。

掌握每個方法的語意與正確使用時機,能讓 API 符合 RESTful 原則、提升可讀性與維護性,同時也有助於前端或第三方服務的整合。本文將從概念說明、實作範例、常見陷阱與最佳實踐,逐步帶你建立一套完整且可擴充的 FastAPI 路由。


核心概念

1. HTTP 方法的語意

方法 語意 常見用途
GET 取得資源,不應該改變伺服器狀態 查詢列表、單筆資料
POST 建立新資源或觸發非冪等操作 新增資料、上傳檔案
PUT 完全取代目標資源 更新全部欄位
PATCH 部分更新目標資源 只改變部份欄位
DELETE 刪除資源 移除資料
OPTIONS 取得目標 URL 支援的 HTTP 方法(CORS 預檢) 前端跨域請求的預檢

FastAPI 直接以裝飾器(@app.get()@app.post()…)對應上述方法,並且可以同時宣告 路徑參數查詢參數請求體回應模型


2. 基本路由宣告

from fastapi import FastAPI

app = FastAPI()

@app.get("/ping")
async def ping() -> str:
    """簡單的健康檢查,回傳字串"""
    return "pong"
  • @app.get("/ping"):宣告此路由只接受 GET 請求。
  • async def:建議使用 非阻塞 的 async 函式,以發揮 FastAPI 的高效能。

3. 使用 Pydantic 定義請求與回應模型

from pydantic import BaseModel

class Item(BaseModel):
    name: str
    price: float
    description: str | None = None
  • BaseModel 讓資料結構在 驗證自動文件化(OpenAPI)上都有保障。

程式碼範例

以下示範 GET、POST、PUT、PATCH、DELETE、OPTIONS 六種方法的實作,涵蓋路徑參數、查詢參數、請求體與回應模型。

3.1 GET:取得單筆與多筆資料

from fastapi import FastAPI, Query
from typing import List

app = FastAPI()

# 假資料庫
FAKE_DB = {
    1: {"name": "Apple", "price": 3.5},
    2: {"name": "Banana", "price": 2.0},
}

@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: int) -> Item:
    """
    依據 item_id 取得單筆商品資訊。
    若不存在則拋出 404。
    """
    data = FAKE_DB.get(item_id)
    if not data:
        raise HTTPException(status_code=404, detail="Item not found")
    return Item(**data)

@app.get("/items", response_model=List[Item])
async def list_items(skip: int = Query(0, ge=0), limit: int = Query(10, le=100)):
    """
    取得商品清單,支援分頁參數 `skip` 與 `limit`。
    """
    items = [Item(**v) for v in list(FAKE_DB.values())[skip : skip + limit]]
    return items
  • @app.get("/items/{item_id}"):路徑參數 item_id 必須是 int,FastAPI 會自動驗證。
  • Query 用於 查詢字串(如 ?skip=0&limit=10),可設定預設值與驗證條件。

3.2 POST:建立新資源

from fastapi import FastAPI, HTTPException, status

app = FastAPI()
next_id = 3  # 模擬自動遞增的主鍵

@app.post(
    "/items",
    response_model=Item,
    status_code=status.HTTP_201_CREATED,
)
async def create_item(item: Item) -> Item:
    """
    接收 JSON 請求體,建立新商品並回傳建立後的資料。
    """
    global next_id
    if any(v["name"] == item.name for v in FAKE_DB.values()):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Item already exists",
        )
    FAKE_DB[next_id] = item.dict()
    item_id = next_id
    next_id += 1
    return Item(**FAKE_DB[item_id])
  • @app.post("/items"):只接受 POST
  • status_code=status.HTTP_201_CREATED:符合 REST 建立資源的慣例。
  • item: Item 讓 FastAPI 自動解析 JSON 並驗證。

3.3 PUT:完整取代資源

@app.put("/items/{item_id}", response_model=Item)
async def replace_item(item_id: int, item: Item) -> Item:
    """
    用提供的完整資料 *完全* 取代指定的商品。
    若目標不存在則回傳 404。
    """
    if item_id not in FAKE_DB:
        raise HTTPException(status_code=404, detail="Item not found")
    FAKE_DB[item_id] = item.dict()
    return Item(**FAKE_DB[item_id])
  • PUT 要求 完整 的資源表示;若缺少欄位,應視為錯誤或使用預設值。

3.4 PATCH:部份更新

from pydantic import BaseModel

class ItemUpdate(BaseModel):
    name: str | None = None
    price: float | None = None
    description: str | None = None

@app.patch("/items/{item_id}", response_model=Item)
async def update_item(item_id: int, changes: ItemUpdate) -> Item:
    """
    只更新傳入的欄位,其餘保持不變。
    """
    stored = FAKE_DB.get(item_id)
    if not stored:
        raise HTTPException(status_code=404, detail="Item not found")
    update_data = changes.dict(exclude_unset=True)  # 只取有值的欄位
    stored.update(update_data)
    return Item(**stored)
  • exclude_unset=True 能過濾掉 未提供 的欄位,避免把 None 寫回資料庫。

3.5 DELETE:刪除資源

@app.delete("/items/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_item(item_id: int):
    """
    刪除指定商品,成功時回傳 204 No Content。
    """
    if item_id not in FAKE_DB:
        raise HTTPException(status_code=404, detail="Item not found")
    del FAKE_DB[item_id]
    return None  # 204 不需要回傳內容
  • 204 No Content刪除成功 的慣用回應碼。

3.6 OPTIONS:CORS 預檢與自訂回應

FastAPI 內建 CORS 中介軟體,但如果想手動回傳支援的方法,可這樣寫:

from fastapi.responses import JSONResponse

@app.options("/items")
async def options_items():
    """
    回傳此路由支援的 HTTP 方法清單,供瀏覽器 CORS 預檢使用。
    """
    return JSONResponse(
        content={"methods": ["GET", "POST", "OPTIONS"]},
        headers={"Allow": "GET, POST, OPTIONS"},
    )
  • OPTIONS 不會觸發路由的業務邏輯,只回傳 允許的 method 與必要的 CORS 標頭。

常見陷阱與最佳實踐

陷阱 說明 解決方案
使用可變的預設值(例如 dict、list)作為請求體 會在多個請求間共享同一個物件,導致資料被意外改寫。 永遠 使用 None 作為預設值,或使用 Field(default_factory=…)
忽略狀態碼的語意 直接回傳 200 會讓前端無法分辨「建立」與「更新」的差異。 依照 REST 規範使用 201 Created、204 No Content、400 Bad Request…
在 PATCH 中直接使用 item.dict() 會把未提供的欄位寫成 None,造成資料遺失。 使用 exclude_unset=Trueexclude_none=True
未設定 response_model 客戶端得不到自動產生的 OpenAPI 文件,且缺少資料驗證。 為每個路由明確指定 response_model,必要時使用 response_model_exclude_unset=True
在路由函式內直接存取全域變數(例如 FAKE_DB 在多工作程式或容器環境下會產生競爭條件。 使用 依賴注入(Depends) 注入資料庫 session,或改用外部資料庫服務。

最佳實踐

  1. 型別安全:盡量在路由參數、請求體與回應模型上使用 Pydantic,讓 FastAPI 自動完成驗證與文件化。
  2. 非阻塞 I/O:所有可能涉及 I/O(資料庫、外部 API)皆使用 async/await,避免阻塞事件迴圈。
  3. 統一錯誤處理:使用 HTTPException 或自訂的 exception handler,讓錯誤訊息保持一致。
  4. 分層設計:路由僅負責「接收與回傳」,業務邏輯放在 service 層,資料存取放在 repository 層,提升可測試性。
  5. 文件化:利用 FastAPI 自動產生的 Swagger UI(/docs)與 ReDoc(/redoc),確保 API 文件永遠與程式碼同步。

實際應用場景

場景 需要的 HTTP 方法 為何選擇這些方法
電商商品管理後台 GET(列表、細節)
POST(新增商品)
PUT(全域更新)
PATCH(庫存、價格調整)
DELETE(下架)
完整的 CRUD 操作符合 REST 原則,前端可依需求呼叫對應方法。
使用者帳號設定 GET(取得設定)
PATCH(變更密碼、偏好)
設定屬性多且常常只改變部份欄位,使用 PATCH 可減少資料傳輸。
即時聊天服務 POST(發送訊息)
GET(拉取訊息)
OPTIONS(跨域檢查)
訊息是不可變資源,使用 POST 建立,GET 只讀,OPTIONS 確保前端跨域順暢。
監控系統健康檢查 GET(/ping)
OPTIONS(檢查支援方法)
簡單的 GET 用於負載平衡器的健康檢查,OPTIONS 可提供 CORS 預檢資訊。

小技巧:在大型專案中,建議將每個資源(如 itemsusers)的路由放在獨立的 routerAPIRouter)模組,然後在主程式中統一掛載。這樣不僅結構清晰,也方便未來的微服務拆解。


總結

FastAPI 以 裝飾器 + 型別提示 的方式,讓我們可以在同一個檔案中快速定義 GET、POST、PUT、PATCH、DELETE、OPTIONS 六大 HTTP 方法的路由。藉由 Pydantic 的模型驗證、async 的非阻塞設計,以及自動產生的 OpenAPI 文件,開發者可以在保持程式簡潔的同時,確保 API 的正確性與可維護性。

  • GET 用於查詢,POST 用於建立,PUT 用於完整取代,PATCH 用於部份更新,DELETE 用於刪除,OPTIONS 用於 CORS 預檢。
  • 正確使用 狀態碼response_model依賴注入,能避免常見的陷阱並提升效能。
  • 在實務上,將路由與業務邏輯分層、使用 APIRouter 組織模組、搭配自動文件化,會讓大型專案的開發與維護變得更順暢。

掌握這些概念與技巧後,你就能以 FastAPI 建立符合 RESTful 標準、易於擴充且效能卓越的 Web API。祝開發順利!