在FastAPI中,依賴注入(Dependency Injection,簡稱DI)是一個讓代碼更整潔、更靈活的核心特性。它幫助我們把代碼中重複的邏輯(比如獲取數據庫連接、驗證用戶身份)集中管理,避免在每個接口裏重複編寫相同的代碼,同時讓測試和擴展變得更容易。
什麼是依賴注入?¶
想象你寫一個API接口,需要從數據庫查詢用戶信息。如果每次都在接口裏寫“連接數據庫→查詢數據→關閉連接”,代碼會很冗餘。依賴注入的思路是:把“獲取數據庫連接”這個功能封裝成一個獨立的“依賴項”,然後在需要的接口裏直接“請求”這個依賴項,而不用自己重複實現。
簡單來說,依賴注入就是“把別人需要的東西(依賴)提前準備好,然後傳給需要它的地方”。在FastAPI裏,這個“準備”和“傳遞”的過程由FastAPI自動完成。
爲什麼FastAPI需要依賴注入?¶
- 代碼複用:比如多個接口都需要數據庫連接,用依賴注入可以只寫一次連接邏輯,所有接口共享。
- 結構清晰:依賴項獨立管理,接口函數只關心業務邏輯,不關心依賴如何獲取。
- 測試方便:測試時可以用“模擬的依賴”代替真實依賴(比如用假數據代替真實數據庫),讓單元測試更簡單。
- 資源管理:比如數據庫連接、認證信息等資源的創建和釋放,由依賴注入統一處理,避免內存泄漏。
核心用法:定義和使用依賴項¶
FastAPI通過Depends類實現依賴注入。我們需要先定義一個“依賴項函數”,然後在接口函數中用Depends(依賴項)聲明依賴。
1. 基礎依賴項:獲取數據庫連接¶
假設我們需要在多個接口中使用數據庫連接,傳統寫法會重複創建連接。用依賴注入後,我們可以這樣做:
from fastapi import FastAPI, Depends
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker, Session
# 1. 定義數據庫連接依賴項
def get_db():
# 創建數據庫連接(每次請求時執行)
engine = create_engine("sqlite:///test.db")
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
db = SessionLocal()
try:
yield db # 返回連接,供接口使用
finally:
db.close() # 請求結束後關閉連接
# 2. 定義數據庫模型
Base = declarative_base()
class Item(Base):
__tablename__ = "items"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True)
# 3. 創建FastAPI應用
app = FastAPI()
# 4. 在接口中使用依賴項
@app.get("/items/{item_id}")
def read_item(item_id: int, db: Session = Depends(get_db)):
# 直接使用db(數據庫連接)查詢數據
item = db.query(Item).filter(Item.id == item_id).first()
return {"id": item.id, "name": item.name}
關鍵點:
- get_db是依賴項函數,用yield返回連接,確保請求結束後自動關閉連接(類似上下文管理器)。
- 接口函數read_item中,db: Session = Depends(get_db)聲明依賴,FastAPI會自動調用get_db()獲取連接並傳入。
2. 依賴項帶參數:根據用戶ID獲取用戶信息¶
如果依賴項需要參數(比如根據用戶ID查詢用戶),可以在依賴項函數中定義參數,FastAPI會自動解析路由參數或查詢參數。
from fastapi import Depends, HTTPException
from pydantic import BaseModel
# 模擬用戶數據(實際項目中可能來自數據庫)
fake_users_db = {
1: {"id": 1, "name": "Alice"},
2: {"id": 2, "name": "Bob"}
}
# 1. 定義依賴項:獲取用戶信息
def get_user(user_id: int):
user = fake_users_db.get(user_id)
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user
# 2. 在接口中使用依賴項(依賴項需要參數)
@app.get("/users/{user_id}")
def read_user(user_id: int, user: dict = Depends(get_user)):
return user
關鍵點:
- get_user依賴項需要user_id參數,FastAPI會自動從路由路徑參數{user_id}中獲取user_id的值,傳給get_user。
- 如果用戶不存在,直接拋出HTTP異常,FastAPI會自動返回錯誤響應。
3. 嵌套依賴:依賴項依賴其他依賴項¶
如果一個依賴項需要另一個依賴項,可以形成“嵌套依賴”,FastAPI會按順序解析。
# 1. 基礎依賴項:獲取數據庫連接
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
# 2. 第二個依賴項:獲取用戶信息(依賴數據庫連接)
def get_current_user(db: Session = Depends(get_db)):
# 假設用戶信息存在數據庫中,這裏從db查詢用戶ID爲1的用戶
user = db.query(User).filter(User.id == 1).first()
return user
# 3. 第三個依賴項:驗證用戶權限(依賴用戶信息)
def get_admin(user: dict = Depends(get_current_user)):
if user.get("role") != "admin":
raise HTTPException(status_code=403, detail="Not admin")
return user
# 4. 接口使用嵌套依賴
@app.get("/admin")
def admin_page(admin: dict = Depends(get_admin)):
return {"message": "Admin page accessed", "user": admin}
關鍵點:
- get_admin依賴get_current_user,而get_current_user依賴get_db,FastAPI會先解析get_db,再解析get_current_user,最後解析get_admin。
- 依賴項的執行順序由依賴鏈決定,確保所有前置依賴都已準備好。
依賴注入的優勢¶
- 減少重複代碼:相同邏輯(如數據庫連接)只需寫一次,所有接口複用。
- 便於測試:單元測試時可替換依賴項爲模擬對象(如用
unittest.mock模擬數據庫連接)。 - 自動資源管理:通過
yield處理資源(如連接關閉),避免手動管理。 - 與FastAPI文檔集成:依賴項的參數和返回值會自動顯示在Swagger UI中,方便接口調試。
最佳實踐¶
- 單一職責:每個依賴項只做一件事(如只負責數據庫連接或只負責用戶認證)。
- 避免過度依賴:複雜依賴鏈可能降低可讀性,可拆分爲多個小依賴項。
- 異步依賴:如果依賴項是異步操作(如異步數據庫查詢),用
async def定義依賴項,FastAPI會自動處理。
依賴注入讓FastAPI代碼更模塊化、更易維護。掌握它後,你會發現處理重複邏輯和複雜業務變得異常輕鬆。接下來可以嘗試在自己的項目中,把數據庫連接、認證邏輯等抽象爲依賴項,體驗代碼簡化的快感!