跳轉到

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()

最佳實踐

  1. 保持依賴函數簡單:每個依賴函數應該專注於單一職責。

  2. 適當使用緩存:對於計算成本高但不常變化的依賴,使用 @lru_cache 裝飾器。

  3. 考慮依賴的生命週期:了解依賴在請求中的生命週期,避免不必要的資源消耗。

  4. 使用類型提示:充分利用 Python 的類型提示功能,提高代碼的可讀性和 IDE 支持。

  5. 分層組織依賴:將相關的依賴組織在一起,形成清晰的依賴層次結構。