FastAPI 支持在完成后执行一些额外步骤的依赖项。
为此,请使用yield代替return,并在之后编写额外的步骤。
提示
确保使用yield一次。
技术细节
任何可用于以下功能的有效函数:
用作FastAPI依赖项是有效的。
事实上,FastAPI 在内部使用了这两个装饰器。
例如,您可以使用它来创建数据库会话并在完成后关闭它。
yield在发送响应之前,只执行包含该语句之前的代码:
async def get_db():
db = DBSession()
try:
yield db
finally:
db.close()
产生的值是注入到路径操作和其他依赖项中的值:
async def get_db():
db = DBSession()
try:
yield db
finally:
db.close()
yield响应传递后执行语句后面的代码:
async def get_db():
db = DBSession()
try:
yield db
finally:
db.close()
提示
您可以使用async或 正常功能。
FastAPI会对每个都做正确的事情,就像普通的依赖一样。
如果您try在依赖项中使用块 with yield,您将收到使用该依赖项时抛出的任何异常。
例如,如果某些代码在中间、另一个依赖项或路径操作中的某个点使数据库事务“回滚”或创建任何其他错误,您将在依赖项中收到异常。
因此,您可以使用except SomeException.
同样,您可以使用finally来确保执行退出步骤,无论是否有异常。
async def get_db():
db = DBSession()
try:
yield db
finally:
db.close()
您可以拥有任何大小和形状的子依赖项和子依赖项的“树”,并且它们中的任何一个或全部都可以使用yield.
FastAPI将确保每个依赖项中的“退出代码”以yield正确的顺序运行。
例如,dependency_c可以对一个依赖dependency_b,并dependency_b于dependency_a:
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(此处命名为dep_b)的值仍然可用。
并且,反过来,dependency_b需要来自dependency_a(此处命名为dep_a)的值可用于其退出代码。
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和return混合。
并且您可能有一个依赖项,它需要其他几个依赖项yield,等等。
您可以拥有所需的任何依赖项组合。
FastAPI将确保一切以正确的顺序运行。
技术细节
这要归功于 Python 的Context Managers。
FastAPI在内部使用它们来实现这一点。
您已经看到可以使用依赖项,yield并且可以使用try捕获异常的块。
它可能是诱人引发HTTPException的退出代码或类似,后yield。但它不会工作。
依赖中的退出代码在Exception Handlers之后yield执行。在退出代码中(在 之后)没有任何捕获依赖项引发的异常。 yield
因此,如果您在HTTPException之后引发 ,则yield捕获HTTPExceptions 并返回 HTTP 400 响应的默认(或任何自定义)异常处理程序将不再用于捕获该异常。
这就是允许任何设置在依赖项(例如数据库会话)中的东西,例如,被后台任务使用。
后台任务在响应发送后运行。所以没有办法提高 anHTTPException因为甚至没有办法改变已经发送的响应。
但是,如果后台任务创建了数据库错误,至少您可以使用 回滚或干净地关闭依赖项中的会话yield,并且可以记录错误或将其报告给远程跟踪系统。
如果您知道某些代码可能引发异常,请执行最正常/“Pythonic”的操作并try在该部分代码中添加一个块。
如果您想在返回响应之前处理自定义异常并可能修改响应,甚至可能引发HTTPException,请创建自定义异常处理程序。
提示
您仍然可以引发异常,包括HTTPException 之前的yield。但不是之后。
执行的顺序或多或少像这个图。时间从上到下流动。每一列都是交互或执行代码的部分之一。
信息
只会向客户端发送一个响应。它可能是错误响应之一,也可能是来自路径操作的响应。
在发送这些响应之一后,不能再发送其他响应。
提示
此图显示HTTPException,但您也可以引发任何其他异常,您为其创建自定义异常处理程序。并且该异常将由该自定义异常处理程序而不是依赖项退出代码处理。
但是如果你引发一个异常处理程序没有处理的异常,它将由依赖项的退出代码处理。
“上下文管理器”是您可以在with语句中使用的任何 Python 对象。
例如,您可以使用with读取文件:
with open("./somefile.txt") as f:
contents = f.read()
print(contents)
在下面,它open("./somefile.txt")创建了一个称为“上下文管理器”的对象。
当with块完成时,它确保关闭文件,即使有异常。
当您使用 来创建依赖项时yield,FastAPI会在内部将其转换为上下文管理器,并将其与其他一些相关工具结合起来。
警告
这或多或少是一个“高级”的想法。
如果您刚刚开始使用FastAPI,您可能想暂时跳过它。
在 Python 中,您可以通过使用两种方法创建一个类__enter__()__exit__()来创建上下文管理器:和。
您还可以通过在依赖函数中使用 或语句在FastAPI依赖中使用它们:yieldwithasync with
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
提示
创建上下文管理器的另一种方法是:
使用它们来装饰带有单个yield.
这就是FastAPI 在内部用于与yield.
但是您不必为 FastAPI 依赖项使用装饰器(并且您不应该这样做)。
FastAPI 将在内部为您完成。