FastAPI依賴注入:簡化代碼結構的實用技巧

在FastAPI中,依賴注入(Dependency Injection,簡稱DI)是一個讓代碼更整潔、更靈活的核心特性。它幫助我們把代碼中重複的邏輯(比如獲取數據庫連接、驗證用戶身份)集中管理,避免在每個接口裏重複編寫相同的代碼,同時讓測試和擴展變得更容易。

什麼是依賴注入?

想象你寫一個API接口,需要從數據庫查詢用戶信息。如果每次都在接口裏寫“連接數據庫→查詢數據→關閉連接”,代碼會很冗餘。依賴注入的思路是:把“獲取數據庫連接”這個功能封裝成一個獨立的“依賴項”,然後在需要的接口裏直接“請求”這個依賴項,而不用自己重複實現。

簡單來說,依賴注入就是“把別人需要的東西(依賴)提前準備好,然後傳給需要它的地方”。在FastAPI裏,這個“準備”和“傳遞”的過程由FastAPI自動完成。

爲什麼FastAPI需要依賴注入?

  1. 代碼複用:比如多個接口都需要數據庫連接,用依賴注入可以只寫一次連接邏輯,所有接口共享。
  2. 結構清晰:依賴項獨立管理,接口函數只關心業務邏輯,不關心依賴如何獲取。
  3. 測試方便:測試時可以用“模擬的依賴”代替真實依賴(比如用假數據代替真實數據庫),讓單元測試更簡單。
  4. 資源管理:比如數據庫連接、認證信息等資源的創建和釋放,由依賴注入統一處理,避免內存泄漏。

核心用法:定義和使用依賴項

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
- 依賴項的執行順序由依賴鏈決定,確保所有前置依賴都已準備好。

依賴注入的優勢

  1. 減少重複代碼:相同邏輯(如數據庫連接)只需寫一次,所有接口複用。
  2. 便於測試:單元測試時可替換依賴項爲模擬對象(如用unittest.mock模擬數據庫連接)。
  3. 自動資源管理:通過yield處理資源(如連接關閉),避免手動管理。
  4. 與FastAPI文檔集成:依賴項的參數和返回值會自動顯示在Swagger UI中,方便接口調試。

最佳實踐

  • 單一職責:每個依賴項只做一件事(如只負責數據庫連接或只負責用戶認證)。
  • 避免過度依賴:複雜依賴鏈可能降低可讀性,可拆分爲多個小依賴項。
  • 異步依賴:如果依賴項是異步操作(如異步數據庫查詢),用async def定義依賴項,FastAPI會自動處理。

依賴注入讓FastAPI代碼更模塊化、更易維護。掌握它後,你會發現處理重複邏輯和複雜業務變得異常輕鬆。接下來可以嘗試在自己的項目中,把數據庫連接、認證邏輯等抽象爲依賴項,體驗代碼簡化的快感!

小夜