本文 AI 產出,尚未審核

FastAPI

單元:請求與回應(Request & Response)

主題:設定自訂 HTTP Headers


簡介

在 Web API 開發中,HTTP Header 是訊息的「隱形」載體,除了攜帶身份驗證資訊、快取指示、內容類型等標準欄位之外,開發者常會自行定義 header 來傳遞額外的上下文資訊(例如版本號、追蹤 ID、語系設定等)。
FastAPI 內建對 ResponseRequest 物件的完整支援,讓我們能輕鬆在路由函式中讀取或設定自訂 header。掌握這項技巧不僅能提升 API 的可觀測性與彈性,也能在微服務環境下實現跨服務的協調與追蹤。

本篇文章將從 概念說明實作範例常見陷阱最佳實踐,一步一步帶你在 FastAPI 中自如地操作自訂 HTTP headers,並提供實務上的應用情境,幫助初學者快速上手,也給中級開發者更多優化的靈感。


核心概念

1. 為什麼需要自訂 Header?

  • 跨服務追蹤:在分散式系統中,常會在每一次請求的 header 中加入 X-Request-IDTrace-Id,讓日誌與追蹤系統能串聯同一筆交易。
  • 功能開關:使用 X-Feature-Flag 之類的欄位,讓前端或其他服務在不改變 API 本體的情況下,啟用或關閉特定功能。
  • 語系與客製化X-LocaleX-Client-Version 等,可協助後端根據客戶端版本或語系返回不同的內容。

2. FastAPI 中的 Header 兩大入口

方向 物件 主要用途
讀取 Requestfrom fastapi import Request 取得客戶端送來的所有 header,或指定單一欄位。
設定 Responsefrom fastapi import Response 在回傳給客戶端時加入自訂 header,或覆寫既有欄位。

Tip:FastAPI 也支援 依賴注入(Dependency Injection) 直接把 header 當作參數注入,適合只需要單一欄位的情況。

3. Header 名稱的慣例

  • 標準慣例:使用 X- 前綴(已逐步被 IANA 官方的 Sec-Proxy- 等取代,但仍廣為使用)。
  • 命名規則:使用大寫字母與連字號,如 X-User-Id,FastAPI 會自動把 x_user_id 轉換回原始名稱。

程式碼範例

以下範例皆以 Python 3.11FastAPI 0.110 為基礎,使用 uvicorn 作為開發伺服器。

1. 基本的 Request Header 讀取

from fastapi import FastAPI, Request, HTTPException

app = FastAPI()

@app.get("/items/")
async def read_items(request: Request):
    # 直接從 request.headers 取得所有 header(類似 dict)
    user_agent = request.headers.get("user-agent")
    if not user_agent:
        raise HTTPException(status_code=400, detail="Missing User-Agent header")
    return {"User-Agent": user_agent}

說明

  • request.headersImmutableHeaders,提供 get()items() 等字典方法。
  • 若 header 名稱大小寫不一致,FastAPI 會自動正規化(User-Agentuser-agentUSER-AGENT 都可取到)。

2. 使用依賴注入直接取得單一 Header

from fastapi import Header, Depends

def get_trace_id(x_trace_id: str = Header(None)):
    # 若未傳遞此 header,預設為 None
    return x_trace_id

@app.get("/trace/")
async def trace_endpoint(trace_id: str = Depends(get_trace_id)):
    return {"trace_id": trace_id or "未提供"}

說明

  • Header 會自動把 x-trace-id 轉成 x_trace_id 參數。
  • 透過 Depends,此邏輯可在多個路由間重複使用,提升可維護性。

3. 在 Response 中加入自訂 Header

from fastapi import Response

@app.post("/login/")
async def login(username: str, response: Response):
    # 假設已完成驗證,產生 JWT token
    token = "eyJhbGciOiJIUzI1NiIsInR5cCI6..."
    # 設定自訂 header
    response.headers["X-Auth-Token"] = token
    # 同時也可以設定標準的 Authorization
    response.headers["Authorization"] = f"Bearer {token}"
    return {"message": "登入成功"}

說明

  • Response 物件會在路由結束時自動回傳給客戶端,直接操作 response.headers 即可。
  • 若同時設定了相同名稱的標準與自訂 header,最後一次賦值會覆寫

4. 使用 JSONResponse 加入多個 Header

from fastapi.responses import JSONResponse

@app.get("/status/")
async def status():
    data = {"service": "order", "status": "healthy"}
    headers = {
        "X-Service-Version": "v2.3.1",
        "Cache-Control": "no-store"
    }
    return JSONResponse(content=data, headers=headers)

說明

  • JSONResponse 接受 headers 參數,可一次性傳入多個欄位。
  • 這種寫法適合 只回傳 JSON 且不需要額外的 Response 物件時使用。

5. 動態產生 Header(例如根據請求內容)

@app.get("/locale/")
async def get_locale(request: Request, response: Response):
    # 讀取客戶端傳來的 X-Locale,若無則預設為 zh-TW
    client_locale = request.headers.get("x-locale", "zh-TW")
    # 根據 locale 設定回傳的 Content-Language
    response.headers["Content-Language"] = client_locale
    # 同時回傳一個說明訊息
    return {"locale_used": client_locale}

說明

  • 這個範例展示 讀取 → 邏輯處理 → 設定 的完整流程。
  • Content-Language 是標準 header,讓瀏覽器或 API 用戶端知道回傳內容的語系。

常見陷阱與最佳實踐

陷阱 說明 解決方案
大小寫不一致 雖然 HTTP header 名稱不區分大小寫,但在程式碼中直接使用字典索引時可能會忘記正規化。 使用 request.headers.get("X-My-Header"),或利用 Header 依賴注入,讓 FastAPI 自動處理。
重複設定同名 Header 多次賦值會覆寫前面的值,導致意外遺失資訊。 若需要 多值(例如 Set-Cookie),請使用 response.headers.append() 或直接建立 MultiDict
忘記 CORS 設定 客戶端跨域請求時,瀏覽器會過濾自訂 header,導致前端拿不到。 CORSMiddleware 中加入 expose_headers=["X-Auth-Token", "X-Trace-Id"]
安全性洩漏 不當地把敏感資訊(如 JWT)放在自訂 header 中,可能被瀏覽器或代理伺服器記錄。 僅在 HTTPS 下傳遞,且盡量使用 Authorization 標準欄位。
忘記設定 Cache-Control 自訂 header 可能被快取代理誤快取,造成舊資料回傳。 明確設定 Cache-Control: no-store 或適當的快取策略。

最佳實踐

  1. 統一命名規則:所有自訂 header 建議以 X- 為前綴,並以大寫加連字號命名。
  2. 集中管理:將常用的 header 名稱與說明寫在 constants.py,例如 HEADER_TRACE_ID = "X-Trace-Id",避免硬編碼。
  3. 使用依賴注入:對於必須在多個路由中讀取的 header(如 X-Request-ID),建議寫成依賴函式,統一驗證與預設值。
  4. 加入 CORS expose_headers:若前端需要讀取自訂 header,務必在 CORSMiddleware 中明確列出。
  5. 測試覆寫行為:使用 TestClient 撰寫單元測試,確保自訂 header 在不同情境下正確回傳。
from fastapi.testclient import TestClient
client = TestClient(app)

def test_login_header():
    resp = client.post("/login/", json={"username": "alice"})
    assert "X-Auth-Token" in resp.headers
    assert resp.headers["X-Auth-Token"].startswith("eyJ")

實際應用場景

1. 分散式追蹤(Distributed Tracing)

在微服務架構中,每一次 API 呼叫都會攜帶 X-Trace-Id。服務 A 接收到請求後,在回應的 header 中回傳相同的 Trace ID,讓呼叫端(如前端或 API Gateway)可以把整條請求鏈路串起來。

@app.middleware("http")
async def add_trace_id(request: Request, call_next):
    trace_id = request.headers.get("X-Trace-Id", str(uuid4()))
    response = await call_next(request)
    response.headers["X-Trace-Id"] = trace_id
    return response

2. 動態功能開關(Feature Flag)

產品團隊想要在特定客戶端先行測試新功能,只需要在請求中加入 X-Feature-Flag: new-dashboard,後端根據此欄位決定回傳的 JSON 結構。

@app.get("/dashboard/")
async def dashboard(x_feature_flag: str = Header(None)):
    if x_feature_flag == "new-dashboard":
        return {"layout": "new", "widgets": [...]}
    return {"layout": "legacy", "widgets": [...]}

3. 多語系內容交付

前端會在每次請求帶上 X-Locale,後端根據此 header 回傳對應語系的文字或日期格式。

@app.get("/greeting/")
async def greeting(request: Request, response: Response):
    locale = request.headers.get("X-Locale", "zh-TW")
    greetings = {"en-US": "Hello", "zh-TW": "哈囉", "ja-JP": "こんにちは"}
    response.headers["Content-Language"] = locale
    return {"message": greetings.get(locale, greetings["zh-TW"])}

總結

  • 自訂 HTTP header 是 API 與外部系統溝通的靈活管道,能承載追蹤、功能開關、語系等額外資訊。
  • 在 FastAPI 中,我們透過 Request.headers 讀取、Response.headers 設定,或使用 依賴注入Header)快速取得單一欄位。
  • 留意 大小寫正規化、重複設定、CORS 暴露 等常見陷阱,並遵循 命名慣例、集中管理、統一驗證 的最佳實踐。
  • 透過範例程式碼與實務情境,我們看到自訂 header 在 分散式追蹤、功能開關、國際化 等場景中的威力。

掌握了這些技巧,你的 FastAPI 專案將更具可觀測性、彈性與可維護性。快把本文的範例套用到自己的服務中,立即感受自訂 header 為 API 帶來的升級吧!