FastAPI 依賴注入設計模式
為什麼使用依賴注入?
依賴注入為我們的 FastAPI 應用提供以下優勢:
- 解耦:將依賴的創建與使用分離,降低模組間的耦合度
- 可測試性:輕鬆替換依賴項以進行單元測試
- 重用性:在多個路由中重複使用相同的依賴
- 可維護性:更清晰的代碼結構和責任分離
常用設計模式
1. 基礎依賴注入模式
最簡單的依賴注入形式,直接在路由函數中使用 Depends
。
- 適用場景:處理簡單的依賴關係,如查詢參數處理、簡單的配置讀取等
from fastapi import FastAPI, Depends
app = FastAPI()
def get_query_params(q: str = None):
return {"q": q}
@app.get("/items/")
def read_items(params: dict = Depends(get_query_params)):
return {"params": params}
2. 單例模式
確保某個依賴在整個應用中只有一個實例,適用於共享資源或配置。
- 適用場景:應用配置、資料庫連接池、共享客戶端等需要全局共享的資源
from fastapi import FastAPI, Depends
from pydantic import BaseSettings
from typing import Dict, Any
app = FastAPI()
# 使用類來實現正式的單例模式
class Settings(BaseSettings):
_instance = None
app_name: str = "Awesome API"
version: str = "1.0.0"
class Config:
# 可以指定從環境變數或.env文件讀取配置
env_file = ".env"
# 實現單例模式
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
# 全局單例實例
settings = Settings()
def get_settings() -> Settings:
return settings
@app.get("/info")
def read_info(settings: Settings = Depends(get_settings)):
return settings.dict()
3. 工廠模式
動態創建依賴實例,可以根據不同條件返回不同的實例。
- 適用場景:需要根據請求上下文動態選擇依賴實現的場景
from fastapi import FastAPI, Depends, Header
from typing import Dict, Optional
app = FastAPI()
# 簡單的資料庫連接類
class DatabaseConnection:
def __init__(self, connection_string: str):
self.connection_string = connection_string
def get_connection_info(self) -> Dict[str, str]:
return {"connection": self.connection_string}
# 資料庫工廠函數
def create_db_connection(environment: str) -> DatabaseConnection:
if environment == "testing":
return DatabaseConnection("test_db_connection")
else:
return DatabaseConnection("production_db_connection")
# FastAPI 依賴函數
def get_db_connection(x_environment: Optional[str] = Header(None)) -> DatabaseConnection:
return create_db_connection(x_environment or "production")
@app.get("/data")
def read_data(db: DatabaseConnection = Depends(get_db_connection)):
return db.get_connection_info()
4. 鏈式依賴模式
一個依賴可以依賴於其他依賴,形成依賴鏈。
- 適用場景:複雜的授權流程、多層業務邏輯處理等。
from fastapi import FastAPI, Depends
app = FastAPI()
def get_token(authorization: str = Header(None)):
return authorization
def get_current_user(token: str = Depends(get_token)):
# 在實際應用中,這裡會驗證 token 並返回用戶
return {"user": "john_doe", "token": token}
@app.get("/users/me")
def read_user_me(current_user: dict = Depends(get_current_user)):
return current_user
5. 裝飾器模式
使用依賴注入來裝飾路由函數,增加額外的功能。
- 適用場景:權限驗證、請求日誌記錄、性能監控等橫切關注點。
from fastapi import FastAPI, Depends, HTTPException
app = FastAPI()
def verify_admin(x_role: str = Header(None)):
if x_role != "admin":
raise HTTPException(status_code=403, detail="Not authorized")
return True
@app.get("/admin", dependencies=[Depends(verify_admin)])
def admin_route():
return {"message": "Welcome, admin!"}
6. 類別依賴模式
使用類別而不是函數來定義依賴,提供更好的封裝和組織。
- 適用場景:複雜的依賴邏輯,需要封裝狀態和行為的場景。
from fastapi import FastAPI, Depends
app = FastAPI()
class DatabaseClient:
def __init__(self, db_name: str = "default"):
self.db_name = db_name
def get_items(self):
return [{"id": 1, "name": f"Item from {self.db_name}"}]
# 可調用類別作為依賴
class DBDependency:
def __init__(self, db_name: str = "default"):
self.db_name = db_name
def __call__(self):
return DatabaseClient(self.db_name)
# 創建依賴實例
get_db = DBDependency()
@app.get("/items")
def read_items(db: DatabaseClient = Depends(get_db)):
return db.get_items()
最佳實踐
-
保持依賴函數簡單:每個依賴函數應該專注於單一職責。
-
適當使用緩存:對於計算成本高但不常變化的依賴,使用
@lru_cache
裝飾器。 -
考慮依賴的生命週期:了解依賴在請求中的生命週期,避免不必要的資源消耗。
-
使用類型提示:充分利用 Python 的類型提示功能,提高代碼的可讀性和 IDE 支持。
-
分層組織依賴:將相關的依賴組織在一起,形成清晰的依賴層次結構。