FastAPI依賴注入詳解:Depends的基礎與高級用法

什麼是依賴注入?

想象一下,你正在寫一個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 抽離出來?這會讓你的代碼更優雅、更強大!

小夜