FastAPI支持的依赖项在完成后会执行一些额外的步骤。
为此,请使用yield而不是return,并在其后编写额外的步骤。
:::tips
提示
确保只使用一次yield
:::
:::tips
为此,您需要使用Python 3.7或更高版本,或者在Python 3.6中安装“反向端口”:pip install async-exit-stack async-generator
这里是安装async-exit-stack和async-generator
:::
:::tips
详细教程
任何可用于以下用途的功能:
@contextlib.contextmanager或者- `@contextlib.asynccontextmanager
用作FastAPI依赖项将是有效的。
实际上,FastAPI在内部使用了这两个装饰器。 :::
具有
例如,您可以使用它来创建数据库会话并在完成后关闭它。yield的数据库依赖性
发送响应之前,仅执行yield语句之前和包括的代码:
产生的值是注入到路径操作和其他依赖项中的值:async def get_db():db = DBSession()try:yield dbfinally:db.close()
传递响应后,将执行async def get_db():db = DBSession()try:yield dbfinally:db.close()
yield语句之后的代码:async def get_db():db = DBSession()try:yield dbfinally:db.close()
:::info 提示
您可以使用async或普通函数。
与正常的依赖项一样,FastAPI将对每种方法执行正确的操作。 :::
依赖中使用
如果在具有yield和tryyield的依赖项中使用try块,则将收到使用依赖项时引发的任何异常。
例如,如果某个代码在中间,另一个依赖项或路径操作中的某个点处发生了数据库事务“回滚”或创建任何其他错误,则您将在依赖项中收到异常。
因此,except SomeException外,您可以在依赖项中查找该特定异常。
以相同的方式,无论是否存在异常,您都可以使用final来确保执行退出步骤。子依赖中使用
您可以具有任意大小和形状的子依赖关系和子依赖关系的“树”,并且它们中的任何一个或全部都可以使用yieldyield。
FastAPI将确保每个具有yield的依赖项中的“退出代码”以正确的顺序运行。
例如,dependency_c可以依赖于dependency_b,而dependency_b可以依赖dependency_a: ```python 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`。<br />在这种情况下,`dependency_c`要执行其退出代码,则需要仍然使用`dependency_b`中的值(在此称为`dep_b`)。<br />并且,`dependency_b`需要来自`dependency_a`的值(在这里称为`dep_a`)可用于其退出代码。```pythonfrom fastapi import Dependsasync def dependency_a():dep_a = generate_dep_a()try:yield dep_afinally:dep_a.close()async def dependency_b(dep_a=Depends(dependency_a)):dep_b = generate_dep_b()try:yield dep_bfinally:dep_b.close(dep_a)async def dependency_c(dep_b=Depends(dependency_b)):dep_c = generate_dep_c()try:yield dep_cfinally:dep_c.close(dep_b)
以同样的方式,您可能会产生yield和return混合的依赖关系。
您可能只有一个依赖关系,而其他几个依赖关系也带有yield等。
您可以具有所需的任何依赖关系组合。
FastAPI将确保一切均以正确的顺序运行。
:::tips
详细教程
这要归功于Python的context manager。
FastAPI在内部使用它们来实现此目的。
:::
与yield和HTTPException的依赖关系
您已经看到可以将依赖关系与yield一起使用,并且可以使用try块来捕获异常。
在yield之后,可能会很想在退出代码中引发HTTPException或类似内容。但这是行不通的。
具有yield依赖关系的退出代码是在异常处理程序之后执行的。在退出代码中(在yield之后),您的依赖项不会引发任何捕获异常。
因此,如果在yield之后引发HTTPException,则捕获HTTPExceptions并返回HTTP 400响应的默认(或任何自定义)异常处理程序将不再具有捕获该异常的功能。
这是允许在依赖项(例如,DB Session(数据库会话) )中设置的任何内容供后台任务使用的方法。
发送响应后,将运行后台任务。因此,没有办法引发HTTPException,因为甚至没有办法更改已经发送的响应。
但是,如果后台任务创建了数据库错误,则至少您可以回退或完全关闭具有yield的依赖项中的会话,还可以记录该错误或将其报告给远程跟踪系统。
如果您知道某些代码可能引发异常,请执行最正常/“Pythonic”的操作,并在代码的该部分中添加try块。
如果您有想要在返回响应并可能修改响应之前处理的自定义异常,甚至可能引发HTTPException,请创建一个自定义异常处理程序。
:::info
提示
您仍然可以在yield之前引发包括HTTPException在内的异常。但不是之后。
:::
执行顺序或多或少类似于此图。时间从上到下流动。每列都是交互或执行代码的一部分。
:::tips
信息
仅一个响应将发送给客户端。它可能是错误响应之一,也可能是路径操作的响应。
发送这些响应之一后,就无法再发送其他响应。
:::
:::tips
提示
该图显示了HTTPException,但是您也可以引发其他任何为其创建“自定义异常处理程序”的异常。而且,该异常将由该自定义异常处理程序处理,而不是依赖关系退出代码处理。
但是,如果引发了异常处理程序未处理的异常,则该异常将由依赖项的退出代码处理。
:::
上下文管理器(Context Manager)
什么是”上下文管理器”
“上下文管理器”是可以在with语句中使用的所有Python对象。
例如,你写一个用with来读取文件的代码:
with open("./somefile.txt") as f:contents = f.read()print(contents)
在下面,open(“./somefile.txt”)创建一个称为“上下文管理器”的对象。
当with块完成时,即使有异常,也要确保关闭文件。
当您创建带有yield的依赖项时,FastAPI会将其内部转换为上下文管理器,并将其与其他一些相关工具组合。
在具有yield的依赖项中使用上下文管理器
:::info
警告
这或多或少是一个“先进”的想法。
如果您只是从FastAPI开始,那么现在可能要跳过它。
:::
在Python中,可以通过使用两种方法创建一个类来创建上下文管理器:__enter__()和__exit__()。
您还可以通过在依赖函数内部使用with或async with语句,在FastAPI依赖项内部使用yield来使用它们:
class MySuperContextManager:def __init__(self):self.db = DBSession()def __enter__(self):return self.dbdef __exit__(self, exc_type, exc_value, traceback):self.db.close()async def get_db():with MySuperContextManager() as db:yield db
:::tips
提示
领一个方式创建上下文管理器是:
@contextlib.contextmanageror@contextlib.asynccontextmanager用单个yield它们来单次装饰一个函数。
这就是FastAPI在内部用于产生收益的依赖关系的方式。
但是,您不必对DecorAPI依赖项使用装饰器(也不应使用)。
FastAPI将在内部为您完成此任务。 :::
