FastAPI 学习教程 · 第4部分

依赖注入系统(Dependency Injection)

💡 本部分目标:理解 FastAPI 强大的依赖注入机制,学会复用逻辑(如认证、数据库连接、配置加载),让代码更模块化、可测试、可维护。


一、什么是"依赖注入"(Dependency Injection)?

在编程中,"依赖"是指一个函数或类需要的外部资源或服务,比如:

  • 数据库连接
  • 当前登录用户信息
  • API 密钥验证
  • 配置参数

依赖注入(DI) 就是:在运行时自动提供这些依赖,而不是在函数内部硬编码创建它们。

FastAPI 的依赖注入有什么好处?

  • 代码复用:多个接口共享同一逻辑(如验证 token)
  • 解耦:业务逻辑与基础设施(如数据库)分离
  • 可测试:可以轻松替换依赖(如用 mock 数据库)
  • 声明式 :只需在函数参数中声明 Depends(...),FastAPI 自动处理

二、基础用法:使用 Depends()

FastAPI 通过 fastapi.Depends 实现依赖注入。

步骤 1:定义一个"依赖函数"

python 复制代码
def get_current_user():
    # 模拟从 token 中解析用户(实际项目会验证 JWT)
    return {"username": "alice", "role": "admin"}

步骤 2:在路径操作函数中使用它

python 复制代码
from fastapi import Depends, FastAPI

app = FastAPI()

@app.get("/profile")
def read_profile(current_user=Depends(get_current_user)):
    return {"user": current_user}

🔍 当访问 /profile 时,FastAPI 会:

  1. 先调用 get_current_user()
  2. 把返回值传给 current_user 参数
  3. 再执行你的路由函数

三、常见场景 1:API Key 验证

很多 API 要求客户端在请求头中携带 X-API-Key

示例:验证 API Key

python 复制代码
from fastapi import Depends, FastAPI, HTTPException, Header

app = FastAPI()

# 依赖函数:验证 API Key
def verify_api_key(x_api_key: str = Header(...)):
    valid_keys = ["secret123", "mykey456"]
    if x_api_key not in valid_keys:
        raise HTTPException(status_code=403, detail="Invalid API Key")
    return x_api_key

# 使用依赖
@app.get("/protected-data", dependencies=[Depends(verify_api_key)])
def get_protected_data():
    return {"data": "这是敏感数据!"}

⚠️ 注意:

  • Header(...) 表示从请求头中读取 x-api-key(FastAPI 会自动转为小写和下划线)
  • 使用 dependencies=[Depends(...)] 表示只执行依赖,不接收返回值

测试方式:

  • 请求头中添加:X-API-Key: secret123
  • 如果 key 无效,返回 403 Forbidden

四、常见场景 2:数据库会话管理

在真实项目中,每个请求通常需要一个数据库会话,并在结束后关闭。

示例:模拟数据库连接

python 复制代码
from fastapi import Depends, FastAPI

# 模拟数据库
class FakeDB:
    def __init__(self):
        self.connected = True
    def query(self, sql: str):
        return f"执行: {sql}"
    def close(self):
        self.connected = False

# 依赖函数:提供数据库会话
def get_db():
    db = FakeDB()
    try:
        yield db  # 提供 db 给路由函数
    finally:
        db.close()  # 请求结束后自动关闭
        print("数据库连接已关闭")

app = FastAPI()

@app.get("/items/")
def read_items(db=Depends(get_db)):
    result = db.query("SELECT * FROM items")
    return {"result": result}

🔑 关键点:

  • 使用 yield 实现"请求开始时创建,结束时清理"
  • 即使发生异常,finally 块也会执行,确保资源释放

五、子依赖(嵌套依赖)

依赖函数本身也可以依赖其他依赖!

示例:先验证 API Key,再获取用户

python 复制代码
def get_api_key(x_api_key: str = Header(...)):
    if x_api_key != "supersecret":
        raise HTTPException(status_code=403, detail="Invalid key")
    return x_api_key

def get_current_user_from_key(api_key: str = Depends(get_api_key)):
    # 根据 api_key 查找用户(简化)
    return {"user_id": "U123", "api_key": api_key}

@app.get("/user-info")
def user_info(user=Depends(get_current_user_from_key)):
    return user

调用 /user-info 时,FastAPI 会:

  1. 调用 get_api_key
  2. 用其结果调用 get_current_user_from_key
  3. 最后执行 user_info

六、依赖注入 vs 普通函数调用?

方式 优点 缺点
普通函数调用 简单直接 无法复用、难以测试、无法自动处理异常/清理
依赖注入 自动管理生命周期、支持嵌套、可复用、可测试 初学稍复杂

最佳实践 :凡是需要跨多个接口复用需要资源管理的逻辑,都应写成依赖。


七、完整示例代码(推荐保存为 main.py

python 复制代码
# main.py
from fastapi import Depends, FastAPI, HTTPException, Header
from typing import Generator

app = FastAPI(title="第4部分:依赖注入系统")

# === 1. 模拟数据库 ===
class DatabaseSession:
    def __init__(self):
        self.id = "DB_SESSION_001"
    def execute(self, query: str):
        return f"[{self.id}] 执行: {query}"
    def close(self):
        print(f"[{self.id}] 数据库连接已关闭")

def get_db() -> Generator[DatabaseSession, None, None]:
    db = DatabaseSession()
    try:
        yield db
    finally:
        db.close()

# === 2. API Key 验证 ===
def verify_token(x_token: str = Header(...)):
    if x_token != "valid-token-123":
        raise HTTPException(status_code=401, detail="无效的 Token")
    return x_token

# === 3. 获取当前用户(依赖 token)===
def get_current_user(token: str = Depends(verify_token)):
    return {"username": "alice", "token": token}

# === 路由 ===

@app.get("/health")
def health_check():
    return {"status": "OK"}

@app.get("/items/", dependencies=[Depends(verify_token)])
def list_items(db=Depends(get_db)):
    data = db.execute("SELECT name FROM items")
    return {"items": [data]}

@app.get("/profile")
def profile(user=Depends(get_current_user)):
    return {"profile": user}

测试说明:

  • 访问 /health → 无需 token

  • 访问 /items//profile → 必须在请求头中添加:

    复制代码
    X-Token: valid-token-123

在 Swagger UI (/docs) 中:

  1. 点击任意受保护接口
  2. 点击 "Authorize"
  3. 输入 valid-token-123
  4. 即可正常测试

八、练习任务(动手实践)

🧠 请先自己尝试完成,再查看下方答案!

任务1:实现"日志记录"依赖

  • 创建一个依赖函数 log_request
  • 每次被调用时,打印 "收到请求: {path}"
  • /logs 路由中使用它(不接收返回值)

任务2:组合依赖 ------ 验证角色

  • 依赖1:get_current_user() 返回 {"username": "bob", "role": "user"}
  • 依赖2:require_admin(user=Depends(get_current_user))
    如果 user["role"] != "admin",抛出 403 错误
  • 路由 /admin-only 使用 require_admin 依赖

任务3(挑战):带参数的依赖

  • 创建依赖 get_pagination(skip: int = 0, limit: int = 10)
  • 返回字典 {"skip": skip, "limit": limit}
  • /products/ 路由中使用它,并返回分页信息

九、练习任务参考答案

任务1 答案

python 复制代码
from fastapi import Request

def log_request(request: Request):
    print(f"收到请求: {request.url.path}")

@app.get("/logs", dependencies=[Depends(log_request)])
def logs_endpoint():
    return {"message": "请求已记录"}

💡 Request 对象由 FastAPI 自动注入,可获取 URL、方法、头等信息。


任务2 答案

python 复制代码
def get_current_user():
    # 模拟当前用户(实际项目从 token 解析)
    return {"username": "bob", "role": "user"}

def require_admin(user=Depends(get_current_user)):
    if user["role"] != "admin":
        raise HTTPException(status_code=403, detail="需要管理员权限")
    return user

@app.get("/admin-only", dependencies=[Depends(require_admin)])
def admin_only():
    return {"message": "欢迎,管理员!"}

🔒 访问 /admin-only 会返回 403,因为模拟用户是 "role": "user"

可修改 get_current_user 返回 "role": "admin" 来测试成功情况。


任务3 答案

python 复制代码
def get_pagination(skip: int = 0, limit: int = 10):
    return {"skip": skip, "limit": limit}

@app.get("/products/")
def list_products(pagination=Depends(get_pagination)):
    return {
        "message": "产品列表",
        "pagination": pagination
    }

测试:

  • /products/{"skip":0, "limit":10}
  • /products/?skip=20&limit=5{"skip":20, "limit":5}

十、小结

在本部分,你学会了:

  • 使用 Depends() 注入依赖函数
  • 实现 API 验证数据库会话管理角色权限控制
  • 使用 yield 实现资源自动清理
  • 构建 嵌套依赖(子依赖)
  • 在 Swagger 中测试带 Header 的接口
相关推荐
binbinaishijie882 小时前
ANSYS Workbench完全入门教程|0基础学习路线图
学习·其他
半夏知半秋3 小时前
kcp学习-通用的kcp lua绑定
服务器·开发语言·笔记·后端·学习
曾浩轩3 小时前
图灵完备Turing Complete 2
学习·图灵完备
A小码哥4 小时前
跟着AI学习谷歌最新的通用商业协议(UCP)实操步骤
人工智能·学习
科技林总4 小时前
【系统分析师】4.2 网络体系结构
学习
且去填词4 小时前
深入理解 GMP 模型:Go 高并发的基石
开发语言·后端·学习·算法·面试·golang·go
Yuzhiyuxia4 小时前
【设计模式】设计模式学习总结
学习·设计模式
小裕哥略帅5 小时前
PMP知识--五大过程组
笔记·学习
Aliex_git5 小时前
提示词工程学习笔记
人工智能·笔记·学习