如何在 FastAPI 中统一处理用户认证与数据库会话,并实现可测试的依赖注入

轻磊吖_3539

轻磊吖_3539

2026-08-15

201人浏览

原创

如何在 FastAPI 中统一处理用户认证与数据库会话,并实现可测试的依赖注入

本文介绍如何通过 FastAPI 全局依赖替代中间件方式实现请求级用户加载与数据库会话管理,并提供基于 pytest + SQLAlchemy 异步会话的端到端可回滚测试方案,确保测试隔离性与生产逻辑一致性。

本文介绍如何通过 fastapi 全局依赖替代中间件方式实现请求级用户加载与数据库会话管理,并提供基于 `pytest` + sqlalchemy 异步会话的端到端可回滚测试方案,确保测试隔离性与生产逻辑一致性。

在现代 FastAPI 应用中,为每个请求自动加载当前用户并绑定数据库会话,不应依赖自定义中间件(尤其是涉及状态传递与生命周期管理时),而应优先采用 全局依赖(Global Dependencies) —— 它更符合 FastAPI 的设计哲学,具备类型安全、依赖注入链清晰、易于测试和调试等优势。

✅ 推荐架构:全局依赖替代中间件

首先,移除 SessionMiddleware 和 AuthenticationMiddleware。取而代之的是两个分层依赖:

  1. get_db():生成并管理异步数据库会话(AsyncSession),由 FastAPI 依赖注入系统自动生命周期管理;
  2. get_current_user():接收 db: AsyncSession,从请求头(如 Authorization: Bearer <token></token>)解析凭证,查询数据库获取用户对象,并挂载至 request.state.user 或直接返回。
from fastapi import Depends, Request, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from api.database.setup import get_session  # your async session factory
from api.models import User

async def get_db() -> AsyncGenerator[AsyncSession, None]:
    async with get_session() as session:
        yield session

async def get_current_user(
    request: Request,
    db: AsyncSession = Depends(get_db)
) -> User:
    auth_header = request.headers.get("Authorization")
    if not auth_header or not auth_header.startswith("Bearer "):
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Missing or invalid token")

    token = auth_header[7:]
    # 示例:根据 token 查询用户(实际应结合 JWT 解析或 OAuth2 验证)
    user = await db.execute(select(User).where(User.token == token))
    user = user.scalar_one_or_none()
    if not user:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")

    request.state.user = user  # 可选:供后续中间件/路由访问
    return user

# 全局注册(main.py 中)
app = FastAPI(
    dependencies=[Depends(get_current_user)]  # ← 所有路由默认执行用户认证!
)

该方案天然支持依赖覆盖(app.dependency_overrides),无需修改任何路由签名,也避免了中间件中手动操作 request.state 带来的类型不安全与调试困难。

? 测试:共享会话 + 自动回滚的可靠方案

测试的核心挑战在于:让测试函数与被测路由使用同一个数据库会话实例,并在测试结束时原子性回滚所有变更。关键在于利用 SQLAlchemy 的嵌套事务(BEGIN SAVEPOINT)配合 @event.listens_for 动态恢复保存点。

以下是一个精简可靠的 session fixture 示例(适配异步场景):

import pytest
from sqlalchemy import text
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
from sqlalchemy.event import listens_for
from sqlalchemy.engine import Engine

from api.main import app
from api.database.setup import get_session

@pytest.fixture
async def session() -> AsyncSession:
    # 使用测试专用数据库 URL(如 test.db 或 test DB 实例)
    engine = create_async_engine("sqlite+aiosqlite:///:memory:", echo=False, connect_args={"check_same_thread": False})

    # 创建异步会话工厂
    AsyncSessionLocal = sessionmaker(
        engine, class_=AsyncSession, expire_on_commit=False
    )

    # 启动连接并开启嵌套事务
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)  # 初始化表

        async with AsyncSessionLocal(bind=conn) as session:
            # 关键:监听事务结束事件,自动重建 savepoint
            @listens_for(session.sync_session, "after_transaction_end")
            def restart_savepoint(session, transaction):
                if transaction.nested and not transaction._parent.nested:
                    session.sync_session.begin_nested()

            # 覆盖 FastAPI 的 get_session 依赖
            app.dependency_overrides[get_session] = lambda: session

            try:
                yield session
            finally:
                app.dependency_overrides.clear()
                await session.rollback()

⚠️ 注意事项:

  • 不要使用 BaseHTTPMiddleware 操作 request.state 来传递会话或用户——它绕过 FastAPI 的依赖解析机制,导致类型提示失效、无法被 Depends 注入、且难以在测试中模拟;
  • request.state 仅适合临时存储(如日志上下文),业务关键对象(如 db, user)必须通过 Depends 显式声明;
  • 测试中务必调用 app.dependency_overrides.clear() 清理覆盖,避免污染其他测试用例;
  • 若使用 PostgreSQL/MySQL,请启用 SAVEPOINT 支持,并确保驱动版本兼容(如 asyncpg>=0.29)。

✅ 总结

  • ✅ 用全局依赖取代中间件:提升类型安全、可维护性与可测试性;
  • ✅ 依赖注入即会话生命周期:get_db() 管理会话创建与关闭,get_current_user() 复用该会话完成认证;
  • ✅ 测试 fixture 统一会话:通过 dependency_overrides + 嵌套事务 + 事件监听,实现“每测试一个事务”的强隔离;
  • ❌ 避免在中间件中手动管理 request.state.db_session 或 request.state.user——这是反模式,易引发资源泄漏与并发问题。

这套方案已在多个中大型 FastAPI 项目中验证,兼顾开发体验、运行效率与测试可靠性。

大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
Python FastAPI异步API开发_Python怎么用FastAPI构建异步API
Python FastAPI异步API开发_Python怎么用FastAPI构建异步API

Python FastAPI 异步开发利用 async/await 关键字,通过定义异步视图函数、使用异步数据库库 (如 databases)、异步 HTTP 客户端 (如 httpx),并结合后台任务队列(如 Celery)和异步依赖项,实现高效的 I/O 密集型 API,显著提升吞吐量和响应速度,尤其适用于处理数据库查询、网络请求等耗时操作,无需阻塞主线程。

2025.12.22

99

5

Python 微服务架构与 FastAPI 框架
Python 微服务架构与 FastAPI 框架

本专题系统讲解 Python 微服务架构设计与 FastAPI 框架应用,涵盖 FastAPI 的快速开发、路由与依赖注入、数据模型验证、API 文档自动生成、OAuth2 与 JWT 身份验证、异步支持、部署与扩展等。通过实际案例,帮助学习者掌握 使用 FastAPI 构建高效、可扩展的微服务应用,提高服务响应速度与系统可维护性。

2026.02.06

494

18

Python Web框架FastAPI 全栈开发教程合集
Python Web框架FastAPI 全栈开发教程合集

以 FastAPI 为核心,讲解现代 Python Web API 的高效开发方式,涵盖路由定义与路径参数/查询参数/请求体绑定、Pydantic 模型的数据校验与序列化、依赖注入(Depends)系统的分层设计、中间件与 CORS 配置、OAuth2 + JWT 认证流程、后台任务(BackgroundTasks)、WebSocket 实时通信、SQLAlchemy 异步 ORM 集成、自动生成 OpenAPI/Swagger 交互文

2026.05.09

456

23

Python FastAPI异步微服务与高性能接口设计
Python FastAPI异步微服务与高性能接口设计

本专题聚焦 Python FastAPI 框架在高性能接口与微服务开发中的应用,讲解异步请求处理、依赖注入机制、路由设计、数据库异步操作以及接口性能优化策略。结合实际项目案例,帮助开发者构建高并发、低延迟的现代化后端服务架构。

2026.06.16

379

12

Buffalo框架数据库开发全教程
Buffalo框架数据库开发全教程

本专题围绕Buffalo框架数据库开发,讲解database.yml多环境配置、soda与fizz迁移生成回滚、模型结构体标签、增删改查与条件查询、一对多与多对多关联、数据校验、回调钩子、事务处理及原生SQL执行能力。

2026.09.23

20

15

Buffalo框架路由与请求处理实操指南
Buffalo框架路由与请求处理实操指南

本专题讲解Buffalo框架路由与请求处理机制,涵盖路由注册与分组、资源路由、Handler编写规范、Context上下文方法、参数绑定、中间件编写挂载、Session与Cookie读写、Flash消息及错误页面定制方法。

2026.09.23

0

15

Buffalo框架零基础入门教程
Buffalo框架零基础入门教程

本专题整理Buffalo框架入门内容,涵盖Go环境准备、buffalo CLI安装、新项目生成、目录结构说明、dev热加载启动、数据库连接配置与常见报错排查,帮助新手按约定优于配置的思路跑通第一个Buffalo框架应用。

2026.09.23

0

15

Conan创建软件包配方指南
Conan创建软件包配方指南

本专题介绍通过conanfile.py创建软件包的方法,讲解包名、版本、依赖和构建设置等基础信息,以及source、build、package、package_info等常用方法的作用及编写思路。

2026.09.22

0

12

Conan二进制包配置指南
Conan二进制包配置指南

本专题介绍Conan根据操作系统、编译器、架构和构建类型生成二进制包的方法,讲解Profile、Settings、Options及Package ID的作用,帮助管理不同平台和编译环境下的包版本。

2026.09.22

20

13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.6万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133.2万人学习