本文 AI 產出,尚未審核

FastAPI 中介層(Middleware)── GZip Middleware 完全教學


簡介

在 Web API 開發中,回傳的資料量往往會直接影響到使用者體驗與伺服器成本。如果每次都把原始 JSON、HTML 或檔案以純文字形式傳送,會造成網路帶寬浪費、延遲增加,尤其在行動裝置或低速網路環境下更為明顯。

FastAPI 內建的 GZip Middleware 能自動把符合條件的回應內容使用 GZip 壓縮,讓傳輸資料量減少 70% 以上,同時對開發者而言只需要少量設定即可完成。本文將從概念說明、實作範例、常見陷阱到最佳實踐,帶你一步步掌握 GZip Middleware 的使用方式,讓你的 API 更快、更省資源。


核心概念

什麼是 Middleware?

Middleware(中介層)是一段在 請求(request) 進入路由處理之前、或 回應(response) 回傳客戶端之前執行的程式碼。它可以攔截、修改、或額外處理請求與回應,常見用途包括認證、日誌、CORS、壓縮等。

GZip 壓縮的原理

GZip 是一種基於 DEFLATE 演算法的資料壓縮格式,能把文字或二進位資料壓縮成更小的位元組。瀏覽器與大多數 HTTP 客戶端都支援 Accept-Encoding: gzip 標頭,當伺服器回傳 Content-Encoding: gzip 時,客戶端會自動解壓縮。

FastAPI 的 GZipMiddleware

FastAPI 直接使用 Starlette(底層框架)提供的 GZipMiddleware。它的工作流程如下:

  1. 檢查請求標頭:若客戶端的 Accept-Encoding 包含 gzip,則表示客戶端願意接受壓縮回應。
  2. 判斷回應大小:只有當回應的 原始內容長度 大於設定的 minimum_size(預設 500 bytes)時,才會啟動壓縮。這是為了避免對極小的回應做壓縮,反而浪費 CPU。
  3. 壓縮與回傳:把回應內容以 GZip 壓縮後,加入 Content-Encoding: gzip 標頭,然後送回客戶端。

程式碼範例

以下示範 5 個實用範例,從最簡單的套用到進階的自訂設定與測試。所有程式碼均以 Python 為例,使用 fastapiuvicorn 以及 httpx(測試用)套件。

1️⃣ 基本使用:一行程式碼啟用 GZip

# main.py
from fastapi import FastAPI
from fastapi.middleware.gzip import GZipMiddleware

app = FastAPI()

# 只要在 FastAPI 實例化之後加入以下 middleware,即可自動壓縮回應
app.add_middleware(GZipMiddleware)

@app.get("/items")
def read_items():
    # 回傳大量文字模擬資料
    return {"message": "A" * 2000}

說明add_middleware(GZipMiddleware) 會使用預設的 minimum_size=500,只要回傳內容超過 500 bytes,就會被壓縮。


2️⃣ 調整 minimum_size:只對大檔案壓縮

# main.py
app.add_middleware(
    GZipMiddleware,
    minimum_size=1024,   # 只壓縮大於 1KB 的回應
)

情境:如果你的 API 常回傳小於 1KB 的 JSON(例如簡單的狀態回應),可以把門檻調高,減少不必要的 CPU 開銷。


3️⃣ 搭配其他 Middleware:先執行 CORS 再壓縮

from fastapi.middleware.cors import CORSMiddleware

# 先加入 CORS,確保跨域請求的標頭正確
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

# 再加入 GZip,讓最終的回應都會被壓縮
app.add_middleware(GZipMiddleware, minimum_size=500)

重點:Middleware 的加入順序會影響執行順序,先處理 CORS 再壓縮,確保 Access-Control-Allow-* 標頭不會因壓縮而遺失。


4️⃣ 自訂回應類型:只壓縮 JSON,排除檔案下載

from fastapi.responses import JSONResponse, StreamingResponse

@app.get("/download")
def download_file():
    # 直接回傳檔案流,不適合壓縮(會破壞二進位檔案)
    file_path = "sample.pdf"
    return StreamingResponse(open(file_path, "rb"), media_type="application/pdf")

@app.get("/json")
def json_data():
    # 只回傳 JSON,會被 GZip 中介層壓縮
    data = {"items": [i for i in range(1000)]}
    return JSONResponse(content=data)

技巧StreamingResponse(或其他二進位回傳)在大多數情況下不需要 GZip 壓縮,因為檔案本身可能已經是壓縮格式(如 PNG、ZIP)。FastAPI 會自動跳過壓縮,但如果你想更保險,可在回傳前自行設定 headers={"Content-Encoding": "identity"}


5️⃣ 測試 GZip 是否生效:使用 httpx 模擬客戶端

import httpx

def test_gzip():
    with httpx.Client(base_url="http://127.0.0.1:8000") as client:
        # 主動告訴伺服器「我接受 gzip」
        response = client.get("/items", headers={"Accept-Encoding": "gzip"})
        # 檢查回傳的標頭
        assert response.headers.get("content-encoding") == "gzip"
        # 解壓縮後的內容
        data = response.json()
        print(data["message"][:20])   # 顯示前 20 個字元

if __name__ == "__main__":
    test_gzip()

說明:若 content-encodinggzip,代表中介層成功壓縮。若你在本機測試時發現沒有壓縮,請確認 請求標頭 包含 Accept-Encoding: gzip,以及回應大小超過 minimum_size


常見陷阱與最佳實踐

陷阱 可能的結果 解決方案或最佳實踐
未傳 Accept-Encoding 伺服器不會壓縮,回傳原始資料 在前端或測試工具中加入 Accept-Encoding: gzip(大多數瀏覽器會自動加上)
壓縮過小的回應 CPU 開銷不成比例,甚至使回傳變慢 調整 minimum_size,或在路由層面自行判斷是否需要壓縮
二進位檔案被壓縮 下載檔案損毀或效能下降 StreamingResponseFileResponse 等二進位回傳不使用 GZip,或在回傳前設定 Content-Encoding: identity
多個 Middleware 順序錯亂 標頭被覆蓋、CORS 失效等 先加入 需要先執行的 Middleware(如 CORS、Authentication),最後加入 GZip
開發環境測試時忘記加 Accept-Encoding 誤以為 GZip 無效 使用 httpxcurl -H "Accept-Encoding: gzip" 或瀏覽器開發者工具確認 content-encoding 標頭

最佳實踐

  1. 依需求調整 minimum_size:對於大量返回 JSON 的 API,建議將門檻設為 1KB~2KB;對於主要回傳文字檔的服務,可保持預設 500 bytes。
  2. 結合 CDN:若你的 API 前面有 CDN,先在 CDN 設定壓縮,避免在應用層重複壓縮。
  3. 監控 CPU 使用率:壓縮是 CPU 密集型操作,部署前可在負載測試中觀察 CPU 峰值,必要時調整門檻或使用更高效的硬體。
  4. 在測試環境驗證:使用自動化測試(如 pytest + httpx)確保 content-encoding: gzip 正常出現,避免因環境差異導致上線後壓縮失效。

實際應用場景

場景 為什麼需要 GZip 實作要點
行動端 API(如即時聊天、社交平台) 手機網路頻寬有限,減少流量成本 設定 minimum_size=800,確保文字訊息超過門檻才壓縮
大規模資料匯出(CSV、JSON) 單次回傳可能達數 MB,直接壓縮可減少 70% 流量 只對 StreamingResponse 前的 JSONResponse 使用 GZip,或在檔案產生後自行 gzip 後回傳
微服務間內部呼叫 服務間頻繁傳遞大量 JSON,降低網路延遲 在內部網路亦加入 Accept-Encoding: gzip,確保所有服務都受益
多語系文字內容(如部落格、文件) 多語系文字往往包含大量重複字元,壓縮率高 直接使用 GZipMiddleware,不需額外設定
高併發 API(如金融行情) 每秒上千筆請求,流量成本與回應速度同等重要 透過 minimum_size 控制壓縮頻率,避免 CPU 瓶頸,同時結合反向代理(Nginx)做二層壓縮備援

總結

  • GZip Middleware 是 FastAPI 中最簡單、最有效的效能優化工具之一,只需要一行程式碼即可讓 API 回應自動壓縮。
  • 了解 Accept-EncodingContent-Encoding 以及 minimum_size 的互動關係,能幫助你在不同場景下取得最佳的壓縮效益與 CPU 成本平衡。
  • 正確 排列 Middleware 順序、避免對已壓縮的二進位檔案再次壓縮、以及在開發與測試階段驗證 content-encoding 標頭,都是避免常見陷阱的關鍵。
  • 在實務上,從行動端節省流量、到微服務間的高併發傳輸,GZip 都能帶來顯著的效能提升與成本降低。

透過本文的概念說明與完整範例,你現在應該能在自己的 FastAPI 專案中自信地加入 GZip Middleware,讓 API 更快、更省資源。祝開發順利,快快上線! 🚀