FastAPI零基础教程(四)- 依赖注入,1天吃透FastAPI灵魂

文章目录

    • 前言
    • 一、阶段学习目标
    • 二、什么是依赖注入?核心优势
      • [2\.1 概念](#2.1 概念)
      • [2\.2 四大核心优势](#2.2 四大核心优势)
    • [三、基础 1:普通函数依赖(最常用)](#三、基础 1:普通函数依赖(最常用))
      • [3\.1 基础分页公共依赖](#3.1 基础分页公共依赖)
      • [3\.2 无返回值依赖(仅做校验)](#3.2 无返回值依赖(仅做校验))
    • [四、基础 2:类依赖(带参数通用校验)](#四、基础 2:类依赖(带参数通用校验))
    • [五、核心重点:yield 生成器依赖(资源管理神器)](#五、核心重点:yield 生成器依赖(资源管理神器))
      • [5\.1 yield 与 return 本质区别](#5.1 yield 与 return 本质区别)
      • [5\.2 生产标准:SQLModel 数据库会话依赖(重中之重)](#5.2 生产标准:SQLModel 数据库会话依赖(重中之重))
    • 六、高阶:嵌套多层依赖(鉴权标准链路)
    • 七、四级依赖作用域(项目分层必备)
      • [7\.1 路由组全局依赖(模块化最佳实践)](#7.1 路由组全局依赖(模块化最佳实践))
      • [7\.2 全局 App 依赖(全站生效)](#7.2 全局 App 依赖(全站生效))
      • [7\.3 接口装饰器依赖(无需接收返回值)](#7.3 接口装饰器依赖(无需接收返回值))
    • [八、综合实战:数据库 \+ 登录鉴权完整案例](#八、综合实战:数据库 + 登录鉴权完整案例)
    • [九、依赖缓存机制 \& 异步说明](#九、依赖缓存机制 & 异步说明)
    • 十、新手高频避坑指南
    • 十一、阶段核心总结

前言

经过前三阶段学习,我们掌握基础接口、Pydantic 请求 / 响应、表单文件、全局异常,但写项目时会遇到大量重复代码:

  1. 每个接口都要写分页参数解析;

  2. 每个接口重复写数据库会话获取、关闭;

  3. 每个需要登录的接口重复解析 Token、查询用户;

  4. 权限校验、日志打印、限流逻辑到处复制粘贴。

这些重复逻辑靠复制维护极其繁琐,而依赖注入 Depends 是 FastAPI 核心灵魂 ,专门解决代码复用、逻辑解耦、资源自动管理。

本阶段 1 天学完,覆盖函数 / 类 /yield 资源依赖、嵌套多层依赖、四级作用域(接口 / 路由 / 全局)、登录鉴权实战,学完就能写出标准化分层后端项目,完美搭配前面学的 SQLModel 数据库。

一、阶段学习目标

  1. 理解依赖注入核心设计思想:解耦、复用、自动生命周期管理;

  2. 掌握基础函数依赖,封装分页、公共查询参数;

  3. 学会类依赖,处理带参数通用校验逻辑;

  4. 吃透yield生成器依赖(数据库会话、文件连接必备);

  5. 掌握嵌套多层依赖(鉴权链路:解析 Token→查用户→校验角色);

  6. 四级依赖作用域:单接口、APIRouter 路由组、全局 App 依赖;

  7. 完整实战:数据库会话依赖 + 登录用户鉴权依赖;

  8. 理清依赖缓存机制、异步依赖执行规则、高频避坑点。

二、什么是依赖注入?核心优势

2.1 概念

依赖(Dependable) :任意可调用对象(函数 / 类),封装一段可复用逻辑

Depends():告诉框架自动执行该逻辑,将返回值注入接口参数,无需手动调用。

2.2 四大核心优势

  1. 代码复用:分页、鉴权、DB 会话写一次,所有接口直接引用;

  2. 逻辑解耦:业务接口只处理核心业务,校验 / 资源逻辑抽离;

  3. 自动生命周期:yield实现资源自动创建 + 释放(数据库自动关闭会话);

  4. 易测试:单元测试可替换 Mock 依赖,不用侵入业务代码。

三、基础 1:普通函数依赖(最常用)

3.1 基础分页公共依赖

多列表接口共用分页参数,无需每个接口重复定义 page、page_size

python 复制代码
from fastapi import FastAPI, Depends
from typing import Annotated

app = FastAPI(title="函数依赖演示")

# 公共分页依赖函数
def get_page_params(page: int = 1, page_size: int = 10):
    """统一分页参数校验,限制每页最大50条"""
    if page_size > 50:
        page_size = 50
    offset = (page - 1) * page_size
    return {"page": page, "page_size": page_size, "offset": offset}

# 商品列表接口复用分页
# 注入分页参数依赖:
# - Annotated 用于为参数附加元数据(类型提示)
# - dict 表示该参数最终会被解析为字典格式
# - Depends(get_page_params) 表示该参数的值由 get_page_params 函数提供(如提取 query 中的 page、page_size 等)
@app.get("/goods")
def list_goods(page_info: Annotated[dict, Depends(get_page_params)]):
    return {"分页信息": page_info, "数据": []}

# 用户列表复用同一套分页
@app.get("/users")
def list_users(page_info: dict = Depends(get_page_params)):
    return {"分页信息": page_info, "用户数据": []}

Annotated 是 Python3.10 + 标准写法,类型提示更清晰,推荐统一使用。

3.2 无返回值依赖(仅做校验)

只执行校验逻辑,不需要返回值,参数名用_接收:

python 复制代码
from fastapi import Depends, HTTPException

def check_maintain():
    """全局维护校验依赖"""
    is_maintain = False
    if is_maintain:
        raise HTTPException(status_code=503, detail="系统维护中,暂时无法访问")

@app.get("/notice")
def get_notice(_ = Depends(check_maintain)):
    return {"msg": "公告正常展示"}

四、基础 2:类依赖(带参数通用校验)

函数无法传参时,使用类作为依赖,实例化时传入配置参数,适合限流、权限校验模板。

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

app = FastAPI()

# 修复后的限流依赖类
class RateLimitCheck:
    def __init__(self, max_times: int):
        self.max_times = max_times
    def __call__(self):
        # 模拟限流逻辑
        current_req = 20
        if current_req > self.max_times:
            raise HTTPException(status_code=429, detail="请求过于频繁")

# 普通接口,最多允许10次请求(正确写法1:dependencies列表)
@app.get("/index", dependencies=[Depends(RateLimitCheck(max_times=10))])
def index():
    return {"data": "首页内容"}

# 管理员接口宽松限流,最多100次(正确写法2:函数形参接收)
@app.get("/admin/dashboard")
def admin_dash(_ = Depends(RateLimitCheck(max_times=100))):
    return {"data": "管理后台数据"}

原理:类实现__call__方法,实例变成可调用对象,FastAPI 自动执行。

五、核心重点:yield 生成器依赖(资源管理神器)

5.1 yield 与 return 本质区别

  • return:函数执行结束,无法执行收尾代码;

  • yield:先执行前面逻辑,返回资源给接口;接口执行完毕后自动执行 yield 后的代码,适合数据库、文件、连接自动释放。

5.2 生产标准:SQLModel 数据库会话依赖(重中之重)

python 复制代码
from sqlmodel import create_engine, Session
from fastapi import Depends
from typing import Annotated

# 数据库引擎(沿用之前SQLModel工程代码)
DB_URL = "sqlite:///./test.db"
engine = create_engine(DB_URL, echo=False)

# 标准DB会话依赖
def get_db():
    # 创建会话资源
    db_session = Session(engine)
    try:
        # 将会话注入接口,接口拿到db_session使用
        yield db_session
    finally:
        # 无论接口正常/报错,最终都会执行关闭会话,杜绝连接泄露
        db_session.close()

# 接口注入数据库会话
@app.get("/user/{uid}")
def get_user(uid: int, db: Annotated[Session, Depends(get_db)]):
    user = db.get(User, uid)
    return user

生产避坑:绝对不要手动创建 Session、手动 close,全部交给 yield 依赖自动管理。

六、高阶:嵌套多层依赖(鉴权标准链路)

依赖可以嵌套依赖,形成链式执行,典型场景:登录校验链路

执行顺序:解析 Token → 查询用户 → 校验用户是否激活 → 校验管理员权限

python 复制代码
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer

app = FastAPI()
# 1. 第一层依赖:从Header提取token
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/login")

# 2. 第二层依赖:解析token获取用户id
def get_token_data(token: str = Depends(oauth2_scheme)):
    # 模拟JWT解析
    if token != "test_token":
        raise HTTPException(status.HTTP_401_UNAUTHORIZED, detail="token无效")
    return {"user_id": 1, "role": "user"}

# 3. 第三层依赖:校验用户是否激活
def get_active_user(token_info = Depends(get_token_data)):
    user_id = token_info["user_id"]
    # 模拟查询数据库用户
    user = {"id": 1, "username": "test", "is_active": True, "role": "user"}
    if not user["is_active"]:
        raise HTTPException(403, "账号已禁用")
    return user

# 4. 第四层依赖:仅管理员可访问
def require_admin(user = Depends(get_active_user)):
    if user["role"] != "admin":
        raise HTTPException(403, "无管理员权限")
    return user

# 普通接口:只需登录用户
@app.get("/profile")
def my_info(user = Depends(get_active_user)):
    return {"用户名": user["username"]}

# 管理员接口:多层嵌套依赖
@app.get("/admin/user_list")
def admin_list(user = Depends(require_admin)):
    return {"管理员操作数据": []}

执行顺序:oauth2_scheme → get_token_data → get_active_user → require_admin。

七、四级依赖作用域(项目分层必备)

依赖支持 4 个层级,作用范围由小到大:

作用域 使用位置 适用场景
接口参数级 接口函数参数 单接口独有依赖
路由组级 APIRouter(dependencies=\[\]) 某模块全部接口统一校验(用户模块、订单模块)
全局应用级 FastAPI(dependencies=\[\]) 全站所有接口通用(系统维护、日志)
接口装饰器级 @app.get(dependencies=\[\]) 单个接口附加校验,不占用返回参数

7.1 路由组全局依赖(模块化最佳实践)

python 复制代码
from fastapi import APIRouter, Depends

# 用户路由组,所有接口必须登录
user_router = APIRouter(
    prefix="/user",
    tags=["用户管理"],
    dependencies=[Depends(get_active_user)]
)

# 该接口自动注入登录校验,无需重复写Depends
@user.get("/info")
def user_info():
    return {"msg": "个人信息"}

# 挂载到主app
app.include_router(user_router)

7.2 全局 App 依赖(全站生效)

python 复制代码
# 所有接口都会执行系统维护校验
app = FastAPI(
    title="全局依赖演示",
    dependencies=[Depends(check_maintain)]
)

7.3 接口装饰器依赖(无需接收返回值)

python 复制代码
# 仅执行限流,不需要接收返回值
@app.get("/goods/list", dependencies=[Depends(RateLimitCheck(20))])
def goods_list():
    return []

八、综合实战:数据库 + 登录鉴权完整案例

整合 yield 数据库依赖、嵌套鉴权依赖、路由分组依赖,企业标准模板

python 复制代码
from fastapi import FastAPI, Depends, HTTPException, status, APIRouter
from fastapi.security import OAuth2PasswordBearer
from sqlmodel import SQLModel, Session, create_engine

# ===================== 1. 数据库配置 =====================
DB_URL = "sqlite:///./stage4.db"
engine = create_engine(DB_URL, echo=False)
SQLModel.metadata.create_all(bind=engine)

# DB会话依赖
def get_db():
    db = Session(engine)
    try:
        yield db
    finally:
        db.close()

# ===================== 2. 鉴权依赖链 =====================
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login")
def parse_token(token = Depends(oauth2_scheme)):
    if token != "admin123":
        raise HTTPException(status.HTTP_401_UNAUTHORIZED, "登录失效")
    return {"uid": 1, "role": "admin"}

def get_current_user(db: Session = Depends(get_db), token_info = Depends(parse_token)):
    # 模拟数据库查询用户
    return {"id": token_info["uid"], "username": "超级管理员"}

# ===================== 3. 路由分组 =====================
# 需要登录的用户模块
user_router = APIRouter(
    prefix="/api/user",
    tags=["用户模块"],
    dependencies=[Depends(get_current_user)]
)

@user_router("/me")
def get_my_info(user = Depends(get_current_user)):
    return {"当前登录用户": user}

# 无需登录的公共路由
public_router = APIRouter(prefix="/api/public", tags=["公共接口"])
@public_router("/notice")
def get_notice():
    return {"公告": "欢迎使用系统"}

# ===================== 4. 项目入口 =====================
app = FastAPI(title="依赖注入综合实战")
app.include_router(user_router)
app.include_router(public_router)

九、依赖缓存机制 & 异步说明

  1. 单请求缓存:同一个请求内,同一依赖只会执行一次,多次 Depends 复用不会重复执行;

  2. 同步 / 异步兼容:依赖函数可以 async、普通 def,FastAPI 自动适配;

  3. yield 只能使用一次,一个依赖内禁止多个 yield;

  4. 异常穿透:依赖内抛出 HTTPException,直接终止请求,不会执行接口业务代码。

十、新手高频避坑指南

  1. ❌ 使用 return 做数据库会话依赖,导致会话无法自动关闭,连接池耗尽;

  2. ❌ 多层嵌套依赖逻辑混乱,鉴权链路建议分层拆分;

  3. ❌ 全局依赖配置敏感接口校验(如登录 / 注册),会导致无法访问登录页;

  4. ❌ 忘记 finally 关闭资源,数据库 / 文件句柄泄露;

  5. ❌ 大量接口重复复制分页、DB 代码,不抽离依赖;

  6. ✅ 数据库、文件资源统一使用 yield 生成器依赖;

  7. ✅ 按模块使用 APIRouter 路由级依赖,区分需要登录 / 公开接口;

  8. ✅ 鉴权逻辑分层封装,形成可复用依赖链。

十一、阶段核心总结

  1. 依赖注入核心:Depends()自动执行复用逻辑,解耦重复代码;

  2. 函数依赖:适合分页、简单校验等无参公共逻辑;

  3. 类依赖:适合可配置通用校验(限流、权限模板);

  4. yield 依赖:资源创建 + 自动释放,数据库会话标准写法;

  5. 嵌套依赖:链式鉴权、多层逻辑分层处理;

  6. 四级作用域:接口 / 路由 / 装饰器 / 全局,灵活控制依赖生效范围;

  7. 企业标准架构:DB 会话依赖 + 登录鉴权依赖 + 路由分组依赖。