在FastAPI中,中間件(Middleware)是一種非常實用的功能,它允許我們在請求到達視圖函數之前和響應返回給客戶端之後,統一處理一些邏輯,比如認證、日誌記錄、錯誤處理等。本文將通過一個實戰案例,帶你實現一個簡單的中間件,用於記錄請求日誌和統計響應時間,讓你的API開發更規範、更便於調試。
一、環境準備¶
首先,確保你已經安裝了FastAPI和Uvicorn(用於運行FastAPI應用的服務器)。如果還沒有安裝,可以通過以下命令安裝:
pip install fastapi uvicorn
二、中間件的作用¶
爲什麼要使用中間件來做請求日誌和響應時間統計呢?想象一下,如果我們在每個視圖函數里都手動記錄這些信息,會產生大量重複代碼,而且一旦需求變化(比如要新增字段),修改起來也會很麻煩。中間件可以幫我們統一處理這些邏輯,所有請求都會自動經過中間件,既簡潔又易於維護。
三、編寫中間件實現請求日誌與響應時間統計¶
我們需要創建一箇中間件類,繼承Starlette提供的BaseHTTPMiddleware(FastAPI基於Starlette開發,所以中間件邏輯由Starlette管理),並在dispatch方法中實現日誌記錄和時間統計的邏輯。
1. 定義中間件類¶
from fastapi import FastAPI
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
import time
class LoggingMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
# 記錄請求開始時間(使用time模塊獲取當前時間戳)
start_time = time.time()
# 調用下一個中間件或視圖函數,獲取響應
response = await call_next(request)
# 計算響應耗時(結束時間 - 開始時間)
duration = time.time() - start_time
# 構造日誌信息
log_info = (
f"[請求日誌] 方法: {request.method} | "
f"路徑: {request.url.path} | "
f"客戶端IP: {request.client.host} | "
f"耗時: {duration:.4f}秒 | "
f"狀態碼: {response.status_code}"
)
# 打印日誌(實際項目中可替換爲logging模塊輸出到文件)
print(log_info)
return response
2. 解釋中間件邏輯¶
- 繼承
BaseHTTPMiddleware:這是Starlette提供的中間件基類,封裝了中間件的基本處理流程,我們只需實現dispatch方法即可。 dispatch方法:這是中間件的核心方法,接收兩個參數:request:當前請求對象(包含請求方法、路徑、客戶端信息等)。call_next:一個異步函數,用於調用下一個中間件或視圖函數,返回處理後的響應。- 時間統計:在請求處理前記錄開始時間,處理完成後記錄結束時間,計算時間差(
duration)。 - 日誌構造:將請求方法、路徑、客戶端IP、耗時、響應狀態碼等信息整合爲一條日誌,方便查看和調試。
四、添加中間件到FastAPI應用¶
創建FastAPI應用後,通過add_middleware方法將我們定義的中間件添加到應用中:
# 創建FastAPI應用實例
app = FastAPI()
# 添加中間件(全局生效)
app.add_middleware(LoggingMiddleware)
五、測試中間件效果¶
接下來,我們定義幾個簡單的視圖函數,測試中間件是否能正確記錄請求日誌和響應時間。
1. 定義測試路由¶
@app.get("/")
async def root():
return {"message": "Hello, FastAPI!"}
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id, "item_name": f"Item {item_id}"}
@app.post("/users")
async def create_user(name: str):
return {"user": {"name": name, "status": "created"}}
2. 運行應用並測試¶
在終端執行以下命令啓動應用:
uvicorn main:app --reload
此時,訪問不同的接口(例如http://localhost:8000/、http://localhost:8000/items/123、http://localhost:8000/users?name=test),觀察控制檯輸出的日誌。
3. 日誌輸出示例¶
訪問http://localhost:8000/後,控制檯可能輸出類似以下內容:
[請求日誌] 方法: GET | 路徑: / | 客戶端IP: 127.0.0.1 | 耗時: 0.0002秒 | 狀態碼: 200
六、擴展與優化¶
以上是最基礎的中間件實現,你可以根據實際需求進行擴展:
- 使用
logging模塊:將print替換爲Python的logging模塊,支持日誌分級(INFO/WARNING/ERROR)、輸出到文件等。 - 記錄請求體:對於POST/PUT請求,可以通過
await request.json()獲取請求數據(注意敏感信息脫敏)。 - 異常處理:在中間件中捕獲視圖函數拋出的異常,記錄錯誤堆棧信息。
- 處理跨域:如果需要跨域支持,可以添加CORS中間件。
總結¶
通過本文,你學習瞭如何使用FastAPI中間件統一實現請求日誌和響應時間統計,避免了重複代碼,提高了代碼的可維護性。中間件是FastAPI(以及Starlette)中處理請求/響應前置和後置邏輯的強大工具,後續可以嘗試將更多通用邏輯(如認證、跨域處理)封裝到中間件中,讓API開發更高效、規範。