FastAPI 依賴注入(Depends):提高代碼復用性
FastAPI 是一個基于 Python 的現代 Web 框架,它不僅語法優雅、支持異步,還擁有強大的依賴注入系統(Dependency Injection)。依賴注入能讓我們的代碼模塊化、職責單一、易于擴展,也極大方便了自動化測試。
本文將結合實際場景,系統講解如何在 FastAPI 中:
- 注入數據庫連接
- 注入權限校驗
- 注入請求上下文
- 優雅編寫單元測試
1. 數據庫連接依賴注入(以 Tortoise ORM 為例)
在 Web 應用中,每個請求都需要與數據庫交互。我們不能將數據庫連接硬編碼在每個視圖中,而是通過依賴注入自動管理事務生命周期。
示例:
# database.py
from tortoise.transactions import in_transaction
async def get_db():
async with in_transaction() as connection:
yield connection
通過 yield 的方式,每個請求都會自動管理這個連接的生命周期,執行完畢后自動關閉。
使用方式:
# api/user.py
from fastapi import APIRouter, Depends
from database import get_db
router = APIRouter()
@router.get("/users")
async def get_users(db=Depends(get_db)):
return await db.execute_query_dict("SELECT * FROM user")
優點:
- 保證事務一致性
- 避免重復創建連接
- 易于測試(可替換 get_db)
2. 權限驗證依賴注入
權限校驗是每個 Web 接口的“守門人”。通過 DI,可以實現靈活、統一的權限控制邏輯。
示例:
# auth.py
from fastapi import Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer
from models import User # 假設你有一個 User 模型
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/login")
defdecode_token(token: str):
# 實際項目中請用 JWT 解碼
if token == "admin-token":
return {"id": 1, "is_admin": True}
elif token == "user-token":
return {"id": 2, "is_admin": False}
returnNone
asyncdefverify_token(token: str = Depends(oauth2_scheme)) -> int:
user = decode_token(token)
ifnot user:
raise HTTPException(status_code=401, detail="Invalid token")
return user["id"]
asyncdefadmin_required(user_id: int = Depends(verify_token)):
user = await User.get(id=user_id)
ifnot user.is_admin:
raise HTTPException(status_code=403, detail="Admin only")
return user
使用方式:
@app.get("/admin")
async def admin_dashboard(current_user=Depends(admin_required)):
return {"msg": f"Welcome admin {current_user.username}"}
優點:
- 權限邏輯集中管理
- 可靈活復用(普通用戶、管理員等)
- 更易調試和測試
3. 請求上下文注入(IP、UA、Headers)
如果你想記錄用戶 IP、來源、設備信息等,可以封裝一個請求上下文依賴。
示例:
# context.py
from fastapi import Request
async def get_request_context(request: Request):
return {
"ip": request.client.host,
"user_agent": request.headers.get("user-agent"),
"headers": dict(request.headers)
}
使用方式:
@app.get("/log")
async def log(ctx = Depends(get_request_context)):
print(f"來自 {ctx['ip']} 的請求,UA: {ctx['user_agent']}")
return {"message": "Logged"}
優點:
- 自動提取請求相關數據
- 避免在每個接口中手動處理
- 支持結構化日志、追蹤、限流等高級特性
4. 單元測試更方便(mock 依賴)
使用依賴注入后,每一個外部服務(如數據庫、權限、上下文)都可以在測試中 mock 掉,不再依賴真實服務,非常適合 CI/CD 環境。
示例:
from fastapi.testclient import TestClient
from main import app
from database import get_db
# Mock 數據庫連接
asyncdefoverride_get_db():
classDummyDB:
asyncdefexecute_query_dict(self, sql):
return [{"id": 1, "username": "test_user"}]
yield DummyDB()
app.dependency_overrides[get_db] = override_get_db
client = TestClient(app)
deftest_get_users():
response = client.get("/users")
assert response.status_code == 200
assert response.json() == [{"id": 1, "username": "test_user"}]
你也可以 override 權限驗證、上下文獲取等依賴項:
from auth import verify_token
# mock 權限校驗返回用戶 id
app.dependency_overrides[verify_token] = lambda: 1
優點:
- 測試更快,不依賴數據庫
- 可控輸入輸出,斷言更精準
- 沒有副作用,測試隔離性強
5. 總結表格
功能點 | 依賴函數 | 作用與優點 |
數據庫連接 | get_db | 自動管理事務,防止資源泄漏,方便測試 |
權限校驗 | verify_token | 校驗登錄狀態,可擴展為 RBAC |
請求上下文 | get_request_context | 自動提取 IP、UA、Headers |
單元測試替換依賴 | dependency_overrides | 隔離依賴項,mock 數據源,適用于 CI 測試場景 |
6. 總結與建議
FastAPI 的依賴注入系統不僅功能強大,還極其優雅。它能幫助你:
- 模塊化業務邏輯
- 隔離接口職責
- 顯著提升可測試性
- 構建高可維護的大型應用
無論是構建微服務、后臺管理系統,還是機器學習接口服務,推薦都采用 DI + 類型注解的方式構建接口和服務邏輯,優雅又實用。