FastAPI Request Header 與 Cookie 參數

除了 URL 路徑、查詢字串和 Request Body 之外,HTTP 請求中還包含了 Header (標頭)Cookie

FastAPI 提供了 HeaderCookie 類別,用法與 QueryPath 非常相似。

Header 參數

要讀取 Header,你需要從 fastapi 匯入 Header

from typing import Annotated
from fastapi import FastAPI, Header

app = FastAPI()

@app.get("/items/")
async def read_items(user_agent: Annotated[str | None, Header()] = None):
    return {"User-Agent": user_agent}

自動轉換 (Underscore to Hyphen)

HTTP Header 通常包含連字號 (如 User-Agent),但在 Python 變數命名中我們使用底線 (user_agent)。

預設情況下,Header 會聰明地將參數名稱中的底線 _ 轉換為連字號 - 去讀取對應的 Header。

也就是說,當你宣告 user_agent 時,FastAPI 會去讀取 User-Agent Header。

如果你不想啟用這個自動轉換功能 (例如你的 Header 真的就有底線),可以設定 convert_underscores=False

@app.get("/items/")
async def read_items(
    strange_header: Optional[str] = Header(None, convert_underscores=False)
):
    return {"strange_header": strange_header}

重複的 Header

如果一個 Header 出現多次 (例如 X-Token: fooX-Token: bar),你可以將型別宣告為 List[str] 來接收所有值。

from typing import List

@app.get("/items/")
async def read_items(x_token: Optional[List[str]] = Header(None)):
    return {"X-Token values": x_token}

讀取 Cookie 的方式與 Header 幾乎一模一樣,只需改用 Cookie 類別。

from typing import Annotated
from fastapi import Cookie, FastAPI

app = FastAPI()

@app.get("/items/")
async def read_items(ads_id: Annotated[str | None, Cookie()] = None):
    return {"ads_id": ads_id}
注意:如果你直接宣告 ads_id: str 而沒有使用 Cookie(...)Header(...),FastAPI 會把它當作 Query Parameter。所以務必明確指定。

使用 Pydantic Model 讀取

當你需要同時讀取多個相關的 Header 或 Cookie 時,將它們封裝到一個 Pydantic Model 中會讓程式碼更有結構,也更容易在不同路由之間重用驗證邏輯。

假設你的應用程式有多個追蹤用的 Cookie,你可以這樣定義:

from pydantic import BaseModel, ConfigDict

class CookieModel(BaseModel):
    # 這裡的變數名稱必須跟 Cookie 的名稱完全一致
    session_id: str
    fatebook_tracker: str | None = None
    goog_analytics: str | None = None

    # 建議加上這個設定,防止客戶端傳送多餘的 Cookie 混淆
    model_config = ConfigDict(extra="forbid")

@app.get("/items/")
async def read_items(cookies: Annotated[CookieModel, Cookie()]):
    return cookies

讀取多個 Header

同樣的技巧也能用在 Header 上。這在需要驗證一整組自定義標頭時非常方便:

class CommonHeaders(BaseModel):
    host: str
    save_data: bool
    if_modified_since: str | None = None
    user_agent: str | None = None
    x_token: list[str] = []

    model_config = ConfigDict(extra="forbid")

@app.get("/headers/")
async def read_headers(headers: Annotated[CommonHeaders, Header()]):
    return headers

為什麼要用 Pydantic Model?

  1. 集中管理:驗證規則(如長度、Regex)可以直接寫在 Model 欄位裡。
  2. 重用性:同樣的一組標頭驗證可以輕鬆應用到多個不同的 API 路由。
  3. 自動文件:Swagger UI 會清楚地列出 Model 中定義的所有標頭與 Cookie,並標註哪些是必填的。
  4. 防止多餘資料:透過 extra="forbid",你可以確保 API 嚴格只處理你定義的參數。

總結

  • 使用 Header(...) 讀取 HTTP 標頭。
  • 使用 Cookie(...) 讀取 Cookie。
  • Header 預設會自動將變數名稱的 _ 轉為 -
  • 兩者的用法與參數驗證功能皆與 QueryPath 相同。