本文 AI 產出,尚未審核

FastAPI 路由(Routing)基礎教學


簡介

FastAPI 中,路由(Routing)是將 HTTP 請求對應到程式碼的核心機制。沒有正確的路由設定,前端的請求就找不到後端的處理函式,整個 API 服務將無法運作。
本單元將說明 @app.get@app.post@app.put@app.delete 等裝飾器的使用方式,讓讀者能快速上手建立 RESTful API,並掌握路由的靈活寫法與常見坑洞。

為什麼要先學路由?

  • 它決定了 URL 結構HTTP 方法 的對應關係。
  • 正確的路由設計能提升 API 的可讀性、可維護性與安全性。
  • FastAPI 內建的路由系統結合 型別提示,讓自動產生的 OpenAPI 文件更加完整。

核心概念

1. FastAPI 應用程式與路由裝飾器

在 FastAPI 中,FastAPI() 會產生一個應用程式實例(app),之後所有的路由都會以 裝飾器(Decorator)的形式掛在 app 上。常見的 HTTP 方法對應如下:

方法 裝飾器 說明
GET @app.get() 用於取得資源(讀取)
POST @app.post() 用於建立新資源(新增)
PUT @app.put() 用於完整取代資源(更新)
PATCH @app.patch() 用於部分更新資源
DELETE @app.delete() 用於刪除資源

Tip:裝飾器的第一個參數是 路徑(path),可以使用 路徑參數(Path Parameter)或 查詢參數(Query Parameter)來取得呼叫方傳遞的資料。


2. 基本路由範例

下面示範最簡單的 GETPOST 路由,說明如何回傳 JSON 與接受請求體(request body)。

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

# ----------------- GET 範例 -----------------
@app.get("/hello")
async def read_hello():
    """
    只回傳一段文字,示範最基本的 GET 路由。
    """
    return {"message": "Hello, FastAPI!"}
# ------------------------------------------------

# ----------------- POST 範例 -----------------
class Item(BaseModel):
    name: str
    price: float
    description: str | None = None

@app.post("/items")
async def create_item(item: Item):
    """
    從請求體取得 JSON,並回傳建立結果。
    FastAPI 會自動根據 Item 類別產生驗證與 OpenAPI 文件。
    """
    # 假設此處寫入資料庫...
    return {"status": "created", "item": item}
# ------------------------------------------------

說明

  • @app.get("/hello") → 當客戶端以 GET 方法請求 /hello 時,read_hello 會被呼叫。
  • @app.post("/items") → 客戶端以 POST 方法送出 JSON(符合 Item 結構)到 /items,FastAPI 會自動把 JSON 轉成 Item 物件。

3. 使用路徑參數(Path Parameter)

路徑參數允許我們在 URL 中直接嵌入變數,例如 /users/{user_id}。FastAPI 會自動把字串轉成指定的型別,若轉換失敗則回傳 422 Unprocessable Entity

@app.get("/users/{user_id}")
async def get_user(user_id: int):
    """
    取得單一使用者資訊。
    - `user_id` 會被 FastAPI 轉為 int,若不是數字會自動回 422。
    """
    # 假設從資料庫撈資料...
    fake_user_db = {1: {"name": "Alice"}, 2: {"name": "Bob"}}
    user = fake_user_db.get(user_id)
    if user:
        return {"user_id": user_id, **user}
    return {"error": "User not found"}, 404

小技巧:若想限制參數的範圍,可使用 PathQueryHeader 的額外參數,例如:

from fastapi import Path

@app.get("/items/{item_id}")
async def read_item(
    item_id: int = Path(..., gt=0, description="大於 0 的商品編號")
):
    return {"item_id": item_id}

4. 多方法共用同一路徑

有時候同一個資源需要支援多種 HTTP 方法(例如 GETPUTDELETE),可以分別寫多個裝飾器,或使用 @app.api_route 一次定義多個方法。

# 分別寫法
@app.get("/products/{pid}")
async def get_product(pid: int):
    return {"pid": pid, "action": "read"}

@app.put("/products/{pid}")
async def update_product(pid: int, data: dict):
    return {"pid": pid, "action": "updated", "data": data}

# 使用 api_route
@app.api_route("/products/{pid}", methods=["GET", "PUT", "DELETE"])
async def product_operations(pid: int, request: Request):
    if request.method == "GET":
        return {"pid": pid, "action": "read"}
    elif request.method == "PUT":
        body = await request.json()
        return {"pid": pid, "action": "updated", "data": body}
    else:  # DELETE
        return {"pid": pid, "action": "deleted"}

5. 路由前綴(Router)與模組化

在大型專案中,將路由切分成多個 APIRouter 可以提升可讀性與維護性。下面示範如何在子模組 users.py 中建立 router,然後在主程式中掛載。

# users.py
from fastapi import APIRouter

router = APIRouter(prefix="/users", tags=["users"])

@router.get("/{uid}")
async def get_user(uid: int):
    return {"uid": uid, "name": f"User{uid}"}

@router.post("/")
async def create_user(name: str):
    return {"uid": 123, "name": name}
# main.py
from fastapi import FastAPI
from users import router as users_router

app = FastAPI()
app.include_router(users_router)

好處

  • 前綴prefix)自動加在每個路由前,避免重複寫路徑。
  • 標籤tags)會出現在自動產生的 Swagger 文件,方便文件化。

常見陷阱與最佳實踐

陷阱 說明 解決方案
路徑參數與查詢參數混用時順序錯誤 FastAPI 只會把 {} 內的視為路徑參數,? 後的才是查詢參數。 確認 URL 結構,必要時使用 Query 明確宣告。
忘記加 await 非同步函式若未加 await,會回傳 coroutine,導致回應不正確。 所有 I/O(DB、外部 API)皆使用 await
過度使用 any 失去型別提示,Swagger 文件會變成 any,降低可讀性。 使用 Pydantic 模型或具體型別(intstrlist[Item])。
路由重複 同一路徑、相同方法多次定義會產生 500 Internal Server Error 透過 IDE 或測試確保唯一性,或使用 APIRouter 分區。
未處理例外 未捕捉的例外會直接返回 500,資訊過於模糊。 使用 HTTPException、自訂例外處理器(@app.exception_handler)。

最佳實踐

  1. 統一命名規則:路徑使用小寫、底線或中線 (/users/{user_id})。
  2. 使用 Pydantic Model:所有請求體與回應皆建議用模型定義,讓驗證與文件同步。
  3. 分層設計:路由只負責 HTTP 介面,商業邏輯放在 service 層或依賴注入(DI)中。
  4. 加入版本號:如 /v1/users,方便未來升級 API。
  5. 測試路由:使用 TestClient 撰寫單元測試,確保每條路由的行為符合預期。

實際應用場景

場景 需求 路由設計示例
部落格系統 文章 CRUD、列表分頁、搜尋 /posts(GET 列表)
/posts/{post_id}(GET、PUT、DELETE)
/posts/search(GET + query)
電商平台 商品瀏覽、下單、付款 /products(GET)
/products/{pid}(GET)
/orders(POST)
/orders/{oid}(GET, PATCH)
即時聊天 取得頻道、發送訊息、取得歷史 /rooms(GET)
/rooms/{rid}/messages(GET, POST)
IoT 裝置管理 註冊裝置、上傳感測資料、查詢狀態 /devices(POST)
/devices/{did}(GET, DELETE)
/devices/{did}/data(POST)

案例說明:假設我們在電商平台實作 商品搜尋,可以結合 查詢參數路徑參數

@app.get("/products/search")
async def search_products(
    q: str = Query(..., min_length=3, description="關鍵字"),
    page: int = Query(1, ge=1),
    size: int = Query(20, le=100)
):
    """
    依關鍵字搜尋商品,支援分頁。
    - `q` 必填且長度至少 3。
    - `page`、`size` 為分頁資訊。
    """
    # 模擬搜尋結果
    results = [{"id": i, "name": f"{q} 商品 {i}"} for i in range((page-1)*size, page*size)]
    return {"page": page, "size": size, "total": 1234, "items": results}

此路由會自動在 Swagger 中呈現搜尋參數,前端開發者只要依照文件呼叫即可。


總結

  • 路由是 FastAPI 的入口,透過 @app.get@app.post 等裝飾器把 HTTP 方法與 URL 直接映射到 Python 函式。
  • 路徑參數、查詢參數與 Pydantic Model 讓資料驗證與文件生成變得輕鬆。
  • APIRouter 為大型專案提供模組化、前綴與標籤的管理方式。
  • 常見的坑包括路由重複、型別未定義、忘記 await,只要遵循 最佳實踐(統一命名、模型化、分層設計、版本化),就能寫出可讀、可維護且安全的 API。

掌握了路由的基本定義與進階寫法後,你就可以開始構建各式各樣的 Web 服務,從簡單的 CRUD 到複雜的微服務架構,都能以 FastAPI 的高效能與自動化文件特性快速交付。祝你開發順利,期待看到你用 FastAPI 打造的精彩作品! 🚀