什麼是依賴注入?¶
想象一下,你正在寫一個API,需要獲取用戶信息。這個用戶信息可能來自數據庫、緩存或者認證服務。如果每個API端點都自己寫獲取用戶信息的代碼,不僅重複,還會導致修改時需要改多處(比如換數據庫)。
依賴注入(Dependency Injection,簡稱DI)的核心思想是:把“依賴”(比如數據庫連接、用戶信息)通過系統自動“注入”到函數中,而不是讓函數自己去創建或獲取依賴。這樣代碼更易複用、解耦,維護起來更方便。
在FastAPI中,實現依賴注入的核心工具是 Depends——它就像一個“中介”,幫你把需要的依賴對象“送”到函數面前。
FastAPI中的Depends基礎¶
使用 Depends 分兩步:定義依賴函數和在路徑函數中聲明依賴。
1. 定義依賴函數¶
依賴函數負責“生產”你需要的對象(比如數據庫連接、用戶信息)。例如,模擬一個數據庫連接:
from fastapi import FastAPI, Depends
# 定義依賴函數:返回數據庫連接對象
def get_db():
# 這裏返回模擬的數據庫連接(實際項目中可能是真實連接)
return {"connection": "MySQL"} # 假設是數據庫連接字典
2. 在路徑函數中使用Depends¶
用 Depends(依賴函數) 聲明路徑函數需要的依賴,FastAPI會自動調用依賴函數並注入結果:
app = FastAPI()
@app.get("/users/me")
def read_current_user(db: dict = Depends(get_db)):
# db就是依賴函數get_db返回的數據庫連接對象
return {"db_connection": db, "message": "當前用戶信息"}
當訪問 /users/me 時,FastAPI會先調用 get_db() 獲取數據庫連接,再把它作爲 db 參數傳給 read_current_user。
依賴注入的參數傳遞¶
依賴函數也可以接收參數,這些參數可以是路徑參數、查詢參數等,由FastAPI自動解析。
示例:根據用戶ID獲取用戶信息¶
假設需要根據路徑參數 user_id 查詢用戶,用戶信息的獲取依賴於數據庫連接:
from fastapi import Depends, HTTPException
# 模擬用戶數據
mock_users = {1: {"id": 1, "name": "Alice"}, 2: {"id": 2, "name": "Bob"}}
# 依賴函數:根據user_id查詢用戶(user_id來自路徑參數)
def get_user(user_id: int = Depends()):
if user_id not in mock_users:
raise HTTPException(status_code=404, detail="用戶不存在")
return mock_users[user_id]
# 路徑函數:聲明依賴
@app.get("/users/{user_id}")
def read_user(user: dict = Depends(get_user)):
return {"user": user}
這裏:
- get_user 函數通過 Depends() 捕獲路徑參數 user_id(因爲 user_id 是路徑參數,FastAPI會自動解析)。
- user_id 作爲參數傳入 get_user,返回對應用戶信息。
高級用法¶
依賴注入不僅能解決基礎複用問題,還支持嵌套依賴、緩存、異步等場景。
1. 嵌套依賴(依賴的依賴)¶
一個依賴可以依賴另一個依賴,FastAPI會自動按順序解析。例如,先獲取數據庫連接,再根據連接獲取用戶:
def get_db():
return {"connection": "MySQL"} # 第一步:獲取數據庫連接
def get_user_by_db(db: dict = Depends(get_db), user_id: int = 1):
# 第二步:用數據庫連接查詢用戶(這裏user_id簡化爲固定值,實際可從參數獲取)
if user_id not in mock_users:
raise HTTPException(status_code=404, detail="用戶不存在")
return mock_users[user_id]
@app.get("/users/{user_id}")
def read_user(user: dict = Depends(get_user_by_db)):
return {"user": user}
執行順序:get_user_by_db 依賴 get_db,所以FastAPI先調用 get_db 獲取數據庫連接,再傳入 get_user_by_db 查詢用戶。
2. 依賴緩存(單例模式)¶
如果依賴函數需要複用(比如數據庫連接池),可以用 lru_cache 實現“單例”,避免重複初始化:
from functools import lru_cache
@lru_cache() # 緩存依賴函數結果
def get_db():
print("初始化數據庫連接...") # 僅打印一次,後續複用結果
return {"connection": "MySQL"}
@app.get("/users/me")
def read_user(db: dict = Depends(get_db)):
return {"db": db}
多次調用 /users/me 時,get_db 只會打印一次“初始化數據庫連接…”,後續直接返回緩存的連接對象。
3. 異步依賴¶
如果依賴函數是異步的(比如調用異步數據庫),Depends 也支持:
import asyncio
async def get_async_db():
await asyncio.sleep(0.1) # 模擬異步數據庫操作
return {"connection": "AsyncMySQL"}
@app.get("/async/users")
async def async_read_user(db: dict = Depends(get_async_db)):
return {"async_db": db}
注意:異步路徑函數(async def)才能處理異步依賴的返回值。
4. 依賴參數驗證¶
結合Pydantic模型,依賴函數可以自動驗證參數合法性:
from pydantic import BaseModel
class UserFilter(BaseModel):
user_id: int = 1 # 定義參數類型和默認值
def get_user_filter(filter: UserFilter = Depends()):
return filter # 返回驗證後的參數
@app.get("/validated/users")
def validated_user(filter: UserFilter = Depends(get_user_filter)):
return {"user_id": filter.user_id}
如果請求參數不符合 UserFilter 定義(比如 user_id 不是整數),FastAPI會自動返回 422 Validation Error。
依賴注入的核心優勢¶
- 代碼複用:同一個依賴被多個路徑函數共享,避免重複代碼。
- 解耦:路徑函數只關心業務邏輯,依賴的獲取交給系統。
- 易測試:測試時可用模擬對象(mock)替換真實依賴(如測試用的假數據庫)。
- 可擴展:新增依賴時,只需修改依賴函數,無需改動所有調用它的路徑函數。
總結¶
Depends 是FastAPI中管理依賴關係的核心工具,從基礎的依賴定義、參數傳遞,到高級的嵌套依賴、異步依賴,掌握這些用法能讓你的API代碼更清晰、更易維護。通過合理設計依賴,你可以把複雜的邏輯拆解成獨立的模塊,讓項目結構更健壯。
下次開發FastAPI應用時,不妨思考:哪些重複的依賴邏輯可以通過 Depends 抽離出來?這會讓你的代碼更優雅、更強大!