FastAPI 依赖注入
FastAPI的依赖注入是一个强大的功能,它允许您在路由函数中声明需要的组件或依赖项。这些依赖项会在请求处理之前自动解析和注入,使代码更加模块化、易于测试和维护。
FastAPI依赖注入的基本概念
依赖注入(Dependency Injection)是一种设计模式,它将对象的创建与使用分离开来。在FastAPI中,依赖注入系统允许您声明函数所需的依赖项,然后框架会自动处理这些依赖项的创建和传递。
FastAPI依赖注入的语法
FastAPI依赖注入主要通过以下几种方式实现:
- 通过参数声明依赖:
from fastapi import Depends, FastAPI
app = FastAPI()
async def common_parameters(q: str = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}
@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
return commons
- 类作为依赖项:
from fastapi import Depends, FastAPI
app = FastAPI()
class CommonQueryParams:
def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):
return {"q": commons.q, "skip": commons.skip, "limit": commons.limit}
- 嵌套依赖:
async def dependency_a():
return {"message": "dependency_a"}
async def dependency_b(a: dict = Depends(dependency_a)):
a_result = a["message"]
return {"message": f"dependency_b with {a_result}"}
@app.get("/nested-dependencies/")
async def read_nested(b: dict = Depends(dependency_b)):
return b
FastAPI依赖注入的小案例
下面是一个更完整的案例,展示了依赖注入在身份验证中的应用:
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from typing import Optional
app = FastAPI()
# 模拟数据库
fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "johndoe@example.com",
"hashed_password": "fakehashed123",
"disabled": False,
}
}
# 创建OAuth2密码流的依赖项
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# 用户模型
class User:
def __init__(self, username: str, email: str, full_name: str, disabled: bool):
self.username = username
self.email = email
self.full_name = full_name
self.disabled = disabled
# 根据token获取当前用户的依赖项
async def get_current_user(token: str = Depends(oauth2_scheme)):
user = fake_users_db.get(token)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication credentials",
headers={"WWW-Authenticate": "Bearer"},
)
return User(**user)
# 检查用户是否激活的依赖项
async def get_current_active_user(current_user: User = Depends(get_current_user)):
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user
# 使用依赖项的路由
@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_active_user)):
return {
"username": current_user.username,
"full_name": current_user.full_name,
"email": current_user.email
}
在这个例子中:
oauth2_scheme
是一个依赖项,它处理获取和验证tokenget_current_user
依赖于oauth2_scheme
,用于根据token获取用户信息get_current_active_user
依赖于get_current_user
,用于检查用户是否激活- 最后,路由处理函数依赖于
get_current_active_user
,从而形成了一个依赖链
这种依赖注入的方式使代码更加模块化,每个组件都有明确的职责,便于单元测试和代码维护。同时,FastAPI会自动处理依赖项的解析、验证和异常处理,使开发者可以专注于业务逻辑的实现。
FastAPI 依赖注入的原理
FastAPI 依赖注入的核心原理其实很简单,可以用三个关键步骤来解释:
1. 函数参数的类型提示检查
当你定义一个带 Depends()
的路由函数时,FastAPI 会检查函数参数的类型提示。这个过程基于 Python 的类型注解系统。
async def read_items(commons: dict = Depends(common_parameters)):
return commons
在这个例子中,FastAPI 看到 Depends(common_parameters)
就知道需要调用 common_parameters
函数并将结果注入到这个位置。
2. 依赖图的构建与解析
FastAPI 会为每个路由构建一个依赖关系图。当请求到达时,它会按照这个图的顺序解析所有依赖项。
比如在嵌套依赖的情况下:
@app.get("/nested/")
async def read_nested(b: dict = Depends(dependency_b)):
return b
async def dependency_b(a: dict = Depends(dependency_a)):
return {"result": f"B depends on {a['result']}"}
async def dependency_a():
return {"result": "A"}
FastAPI 会先解析 dependency_a
,然后将结果传给 dependency_b
,最后将 dependency_b
的结果传给路由函数。
3. 依赖的执行与结果传递
当请求到达时,FastAPI 会:
- 执行依赖函数
- 捕获其返回值
- 将这个返回值作为参数传递给需要它的下一个函数
这个过程利用了 Python 的异步特性,所以依赖可以是同步或异步函数。
# 当请求到达 /items/ 路径时
@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
# FastAPI 会先调用 common_parameters 函数
# 然后将返回值作为 commons 参数传入 read_items
return commons
本质总结
FastAPI 依赖注入的本质是函数调用的自动化与编排。它使用了 Python 的类型注解和 Pydantic 的数据验证能力,将一系列函数调用编排成一个有序的执行流程,然后自动处理函数间的参数传递。
这种设计让你可以将复杂逻辑分解为小型、专注的组件,每个组件只负责一个特定任务,而不需要关心这些组件如何被组装和调用的细节。这正是依赖注入模式的核心价值:关注点分离与代码重用。