FastAPI支持的依赖项在完成后会执行一些额外的步骤。
为此,请使用yield而不是return,并在其后编写额外的步骤。

:::tips 提示
确保只使用一次yield :::
:::tips 为此,您需要使用Python 3.7或更高版本,或者在Python 3.6中安装“反向端口”:
pip install async-exit-stack async-generator
这里是安装async-exit-stackasync-generator :::
:::tips 详细教程
任何可用于以下用途的功能:

  • @contextlib.contextmanager或者
  • `@contextlib.asynccontextmanager
    用作FastAPI依赖项将是有效的。
    实际上,FastAPI在内部使用了这两个装饰器。 :::

    具有yield的数据库依赖性

    例如,您可以使用它来创建数据库会话并在完成后关闭它。
    发送响应之前,仅执行yield语句之前和包括的代码:
    1. async def get_db():
    2. db = DBSession()
    3. try:
    4. yield db
    5. finally:
    6. db.close()
    产生的值是注入到路径操作和其他依赖项中的值:
    1. async def get_db():
    2. db = DBSession()
    3. try:
    4. yield db
    5. finally:
    6. db.close()
    传递响应后,将执行yield语句之后的代码:
    1. async def get_db():
    2. db = DBSession()
    3. try:
    4. yield db
    5. finally:
    6. db.close()

    :::info 提示
    您可以使用async或普通函数。
    与正常的依赖项一样,FastAPI将对每种方法执行正确的操作。 :::

    依赖中使用yieldtry

    如果在具有yield的依赖项中使用try块,则将收到使用依赖项时引发的任何异常。
    例如,如果某个代码在中间,另一个依赖项或路径操作中的某个点处发生了数据库事务“回滚”或创建任何其他错误,则您将在依赖项中收到异常。
    因此,except SomeException外,您可以在依赖项中查找该特定异常。
    以相同的方式,无论是否存在异常,您都可以使用final来确保执行退出步骤。

    子依赖中使用yield

    您可以具有任意大小和形状的子依赖关系和子依赖关系的“树”,并且它们中的任何一个或全部都可以使用yield
    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)

  1. 他们都可以利用`yield`。<br />在这种情况下,`dependency_c`要执行其退出代码,则需要仍然使用`dependency_b`中的值(在此称为`dep_b`)。<br />并且,`dependency_b`需要来自`dependency_a`的值(在这里称为`dep_a`)可用于其退出代码。
  2. ```python
  3. from fastapi import Depends
  4. async def dependency_a():
  5. dep_a = generate_dep_a()
  6. try:
  7. yield dep_a
  8. finally:
  9. dep_a.close()
  10. async def dependency_b(dep_a=Depends(dependency_a)):
  11. dep_b = generate_dep_b()
  12. try:
  13. yield dep_b
  14. finally:
  15. dep_b.close(dep_a)
  16. async def dependency_c(dep_b=Depends(dependency_b)):
  17. dep_c = generate_dep_c()
  18. try:
  19. yield dep_c
  20. finally:
  21. dep_c.close(dep_b)

以同样的方式,您可能会产生yieldreturn混合的依赖关系。
您可能只有一个依赖关系,而其他几个依赖关系也带有yield等。
您可以具有所需的任何依赖关系组合。
FastAPI将确保一切均以正确的顺序运行。

:::tips 详细教程
这要归功于Python的context manager
FastAPI在内部使用它们来实现此目的。 :::

yieldHTTPException的依赖关系

您已经看到可以将依赖关系与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来读取文件的代码:

  1. with open("./somefile.txt") as f:
  2. contents = f.read()
  3. print(contents)

在下面,open(“./somefile.txt”)创建一个称为“上下文管理器”的对象。
with块完成时,即使有异常,也要确保关闭文件。
当您创建带有yield的依赖项时,FastAPI会将其内部转换为上下文管理器,并将其与其他一些相关工具组合。

在具有yield的依赖项中使用上下文管理器


:::info 警告
这或多或少是一个“先进”的想法。
如果您只是从FastAPI开始,那么现在可能要跳过它。 :::

在Python中,可以通过使用两种方法创建一个类来创建上下文管理器:__enter__()__exit__()
您还可以通过在依赖函数内部使用withasync with语句,在FastAPI依赖项内部使用yield来使用它们:

  1. class MySuperContextManager:
  2. def __init__(self):
  3. self.db = DBSession()
  4. def __enter__(self):
  5. return self.db
  6. def __exit__(self, exc_type, exc_value, traceback):
  7. self.db.close()
  8. async def get_db():
  9. with MySuperContextManager() as db:
  10. yield db


:::tips 提示
领一个方式创建上下文管理器是:

  • @contextlib.contextmanager or
  • @contextlib.asynccontextmanager 用单个yield它们来单次装饰一个函数。
    这就是FastAPI在内部用于产生收益的依赖关系的方式。
    但是,您不必对DecorAPI依赖项使用装饰器(也不应使用)。
    FastAPI将在内部为您完成此任务。 :::