FastAPI依赖注入系统系列(六) 带有yield功能的依赖项
一、yield在依赖项中的使用
FastAPI支持依赖项在完成请求后做一些额外的工作。为了做到这些使用yield而不是用return,并且写一些额外的步骤。
使用yield功能需要使用python3.7或者之上的版本支持,如果在python3.6需要安装以下的工具:
pip install async-exit-stack async-generator
对于yield的使用:
async def get_db(): db = DBSession() try: yield db finally: db.close()
你可以使用yield去创建一个数据库连接,并且在完成这个过程后去关闭它。
- 在发送响应之前只执行yield之前的代码
- yield产生的值会注入到路径操作或者其它依赖项
- yield后面的代码会在响应后执行
另外你也可以在子依赖项中来进行使用:
from fastapi import Depends async def dependency_a(): dep_a = generate_dep_a() try: yield dep_a finally: dep_a.close() async def dependency_b(dep_a=Depends(dependency_a)): dep_b = generate_dep_b() try: yield dep_b finally: dep_b.close(dep_a) async def dependency_c(dep_b=Depends(dependency_b)): dep_c = generate_dep_c() try: yield dep_c finally: dep_c.close(dep_b)
上面所有的依赖项都可以使用yield来实现,在这个例子中,当dependency_c执行它的退出代码,仍然需要保证dependency_b仍然是可用的。同理dependency_b执行退出代码dependency_a的值也是可用的。
同样的,你可以使用yield与return进行混合使用,或者创建一个依赖项和几个其它yield的子依赖项。总之,可以进行依赖项之间的任意组合,FastAPI会确保这些依赖项按照正确的顺序执行。
不过执行过程中难免出现异常情况,如果在带有yield的依赖项中使用try块的话,你将会在依赖项中接收到随时抛出的异常。所以你可以使用finally来确保不管是否有异常,都执行退出的代码。
async def get_db(): db = DBSession() try: yield db finally: db.close()
注意的是,如果在yield之后抛出一些异常,如HTTPException等,这是行不通的,因为异常处理是是yield之前进行的,如果在yield之后抛出异常,没有任何处理机制去捕获这些异常。
二、上下文管理器
上下文管理器是任何你可以在with语句中使用的python对象。
比如,可以使用with来读一个文件:
with open("./example.txt") as f: contents = f.read()
通过open("./example.txt")创建一个上下问管理器的对象,当这个with块完成后,即使执行发生异常,它也能确保关闭这个文件。在FastAPI中,当你创建一个带yield功能的依赖项,其内部就是将它转成一个上下文管理器
在Python中你可以通过__enter__()和__exit__()两个方法创建一个上下文管理器。然后将它应用到FastAPI中带yield功能的依赖项中。
class MySuperContextManager: def __init__(self): self.db = DBSession() def __enter__(self): return self.db def __exit__(self, exc_type, exc_value, traceback): self.db.close() async def get_db(): with MySuperContextManager() as db: yield db