fastapi 如何让依赖支持 yield(异步资源管理)

发布时间 - 2026-01-26 00:00:00    点击率:
FastAPI依赖中不能直接使用async def + yield,必须用@asynccontextmanager包装异步生成器;正确做法是定义异步上下文管理器,通过Depends注入,确保请求开始时初始化、响应后清理资源。

FastAPI 依赖中用 yield 会报错:TypeError: async generator object is not awaitable

直接在 FastAPI 依

赖函数里写 async def + yield 是不行的。FastAPI 默认只识别同步生成器(def + yield)或纯异步协程(async def + return),不支持异步生成器(async def + yield)。这是底层 Starlette 的限制,不是 FastAPI 的 bug。

正确写法:用 Depends 包裹 contextlib.asynccontextmanager

要让异步资源(比如 aiohttp.ClientSessionaiomysql.PoolAsyncSession)安全地生命周期绑定到请求,必须走异步上下文管理器路线。FastAPI 本身不原生解析 async with,但能识别被 asynccontextmanager 装饰后的函数。

  • 定义依赖时,用 @asynccontextmanager 包装一个 async def 函数,里面 yield 资源实例
  • 在路由中用 Depends(your_async_context_manager) 注入
  • FastAPI 会在请求开始时 await 进入上下文,在响应返回后 await 退出上下文(包括异常路径)
from contextlib import asynccontextmanager
from fastapi import Depends

@asynccontextmanager async def get_db_session(): session = AsyncSessionLocal() try: yield session finally: await session.close()

@app.get("/items/") async def read_items(db: AsyncSession = Depends(get_db_session)): return await db.execute("SELECT * FROM items")

别用 async def + yield 直接当依赖,也别手动 await 启动

以下写法全是错的:

  • async def bad_dep(): yield ... → FastAPI 不识别,抛出 TypeError
  • 试图在依赖里 await 一个 async with 块再 return → 生命周期失控,资源可能提前释放或泄漏
  • asynccontextmanager 返回的对象直接 await → 它不是协程,是上下文管理器实例

关键点在于:asynccontextmanager 把异步生成器转成了符合 ASGI 生命周期预期的上下文管理协议对象,FastAPI 内部会按需调用 __aenter____aexit__

注意嵌套依赖和作用域:scope="app" 不适用异步资源

异步资源(如数据库连接池、HTTP 客户端)通常不能设为 scope="app",因为它们需要按请求隔离或至少按任务隔离。FastAPI 的依赖作用域目前只支持 "app""request"(默认),没有 "task" 级别。

  • 如果多个请求共用一个 aiohttp.ClientSession,没问题 —— 它本就是线程/协程安全的共享对象
  • 但如果共用一个 AsyncSession,就可能引发状态污染或事务冲突
  • 所以大多数情况应保持默认 scope="request",让每个请求拿到独立的 yield 实例

真正容易被忽略的是异常传播:asynccontextmanageryield 后的代码(finally 块)一定会执行,但若 yield 期间抛出未捕获异常,__aexit__ 仍会被调用,此时你得确保清理逻辑能应对“资源处于半初始化/半失效”状态。


# mysql  # app  # session  # ai  # 路由  # 作用域  # 异步协程  # fastapi  # Object  # finally  # 线程  # 对象  # 异步  # 数据库  # http  # bug  # 管理器  # 抛出  # 的是  # 这是  # 共用一个  # 多个  # 设为  # 会在  # 要让  # 不支持 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: Laravel Asset编译怎么配置_Laravel Vite前端构建工具使用  Laravel如何保护应用免受CSRF攻击?(原理和示例)  Laravel如何获取当前登录用户信息_Laravel Auth门面使用与Session用户读取【技巧】  免费的流程图制作网站有哪些,2025年教师初级职称申报网上流程?  Laravel如何使用缓存系统提升性能_Laravel缓存驱动和应用优化方案  jquery插件bootstrapValidator表单验证详解  Laravel如何处理CORS跨域问题_Laravel项目CORS配置与解决方案  Laravel项目结构怎么组织_大型Laravel应用的最佳目录结构实践  Laravel定时任务怎么设置_Laravel Crontab调度器配置  Laravel API路由如何设计_Laravel构建RESTful API的路由最佳实践  Laravel Eloquent性能优化技巧_Laravel N+1查询问题解决  Laravel如何操作JSON类型的数据库字段?(Eloquent示例)  Laravel的路由模型绑定怎么用_Laravel Route Model Binding简化控制器逻辑  谷歌浏览器下载文件时中断怎么办 Google Chrome下载管理修复  韩国服务器如何优化跨境访问实现高效连接?  Laravel如何创建自定义Artisan命令?(代码示例)  Laravel如何配置和使用缓存?(Redis代码示例)  如何快速建站并高效导出源代码?  如何在HTML表单中获取用户输入并结合JavaScript动态控制复利计算循环  如何快速打造个性化非模板自助建站?  大型企业网站制作流程,做网站需要注册公司吗?  香港服务器租用每月最低只需15元?  微信小程序 HTTPS报错整理常见问题及解决方案  制作电商网页,电商供应链怎么做?  使用豆包 AI 辅助进行简单网页 HTML 结构设计  哪家制作企业网站好,开办像阿里巴巴那样的网络公司和网站要怎么做?  javascript和jQuery中的AJAX技术详解【包含AJAX各种跨域技术】  如何在浏览器中启用Flash_2025年继续使用Flash Player的方法【过时】  如何生成腾讯云建站专用兑换码?  在centOS 7安装mysql 5.7的详细教程  php 三元运算符实例详细介绍  安克发布新款氮化镓充电宝:体积缩小 30%,支持 200W 输出  Laravel如何实现URL美化Slug功能_Laravel使用eloquent-sluggable生成别名【方法】  矢量图网站制作软件,用千图网的一张矢量图做公司app首页,该网站并未说明版权等问题,这样做算不算侵权?应该如何解决?  Laravel如何与Inertia.js和Vue/React构建现代单页应用  Laravel观察者模式如何使用_Laravel Model Observer配置  Laravel怎么为数据库表字段添加索引以优化查询  Laravel安装步骤详细教程_Laravel环境搭建指南  Laravel怎么进行数据库回滚_Laravel Migration数据库版本控制与回滚操作  宙斯浏览器怎么屏蔽图片浏览 节省手机流量使用设置方法  Laravel如何使用Socialite实现第三方登录?(微信/GitHub示例)  google浏览器怎么清理缓存_谷歌浏览器清除缓存加速详细步骤  Laravel怎么返回JSON格式数据_Laravel API资源Response响应格式化【技巧】  香港服务器如何优化才能显著提升网站加载速度?  Laravel Eloquent关联是什么_Laravel模型一对一与一对多关系精讲  Laravel如何使用withoutEvents方法临时禁用模型事件  香港服务器网站测试全流程:性能评估、SEO加载与移动适配优化  Laravel如何编写单元测试和功能测试?(PHPUnit示例)  Angular 表单中正确绑定输入值以确保提交与验证正常工作  Laravel怎么实现支付功能_Laravel集成支付宝微信支付