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。它的工作流程如下:
- 檢查請求標頭:若客戶端的
Accept-Encoding包含gzip,則表示客戶端願意接受壓縮回應。 - 判斷回應大小:只有當回應的 原始內容長度 大於設定的
minimum_size(預設 500 bytes)時,才會啟動壓縮。這是為了避免對極小的回應做壓縮,反而浪費 CPU。 - 壓縮與回傳:把回應內容以 GZip 壓縮後,加入
Content-Encoding: gzip標頭,然後送回客戶端。
程式碼範例
以下示範 5 個實用範例,從最簡單的套用到進階的自訂設定與測試。所有程式碼均以 Python 為例,使用 fastapi、uvicorn 以及 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-encoding為gzip,代表中介層成功壓縮。若你在本機測試時發現沒有壓縮,請確認 請求標頭 包含Accept-Encoding: gzip,以及回應大小超過minimum_size。
常見陷阱與最佳實踐
| 陷阱 | 可能的結果 | 解決方案或最佳實踐 |
|---|---|---|
未傳 Accept-Encoding |
伺服器不會壓縮,回傳原始資料 | 在前端或測試工具中加入 Accept-Encoding: gzip(大多數瀏覽器會自動加上) |
| 壓縮過小的回應 | CPU 開銷不成比例,甚至使回傳變慢 | 調整 minimum_size,或在路由層面自行判斷是否需要壓縮 |
| 二進位檔案被壓縮 | 下載檔案損毀或效能下降 | 對 StreamingResponse、FileResponse 等二進位回傳不使用 GZip,或在回傳前設定 Content-Encoding: identity |
| 多個 Middleware 順序錯亂 | 標頭被覆蓋、CORS 失效等 | 先加入 需要先執行的 Middleware(如 CORS、Authentication),最後加入 GZip |
開發環境測試時忘記加 Accept-Encoding |
誤以為 GZip 無效 | 使用 httpx、curl -H "Accept-Encoding: gzip" 或瀏覽器開發者工具確認 content-encoding 標頭 |
最佳實踐
- 依需求調整
minimum_size:對於大量返回 JSON 的 API,建議將門檻設為 1KB~2KB;對於主要回傳文字檔的服務,可保持預設 500 bytes。 - 結合 CDN:若你的 API 前面有 CDN,先在 CDN 設定壓縮,避免在應用層重複壓縮。
- 監控 CPU 使用率:壓縮是 CPU 密集型操作,部署前可在負載測試中觀察 CPU 峰值,必要時調整門檻或使用更高效的硬體。
- 在測試環境驗證:使用自動化測試(如 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-Encoding、Content-Encoding 以及 minimum_size 的互動關係,能幫助你在不同場景下取得最佳的壓縮效益與 CPU 成本平衡。
- 正確 排列 Middleware 順序、避免對已壓縮的二進位檔案再次壓縮、以及在開發與測試階段驗證
content-encoding標頭,都是避免常見陷阱的關鍵。 - 在實務上,從行動端節省流量、到微服務間的高併發傳輸,GZip 都能帶來顯著的效能提升與成本降低。
透過本文的概念說明與完整範例,你現在應該能在自己的 FastAPI 專案中自信地加入 GZip Middleware,讓 API 更快、更省資源。祝開發順利,快快上線! 🚀