FastAPI教程 与yield的依赖关系

2021-11-03 09:37 更新

FastAPI 支持在完成后执行一些额外步骤的依赖项。

为此,请使用yield代替return,并在之后编写额外的步骤。

提示

确保使用yield一次。

技术细节

任何可用于以下功能的有效函数:

用作FastAPI依赖项是有效的。

事实上,FastAPI 在内部使用了这两个装饰器。

数据库依赖项 yield

例如,您可以使用它来创建数据库会话并在完成后关闭它。

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会对每个都做正确的事情,就像普通的依赖一样。

依赖yield和try

如果您try在依赖项中使用块 with yield,您将收到使用该依赖项时抛出的任何异常。

例如,如果某些代码在中间、另一个依赖项或路径操作中的某个点使数据库事务“回滚”或创建任何其他错误,您将在依赖项中收到异常。

因此,您可以使用except SomeException.

同样,您可以使用finally来确保执行退出步骤,无论是否有异常。

async def get_db():
    db = DBSession()
    try:
        yield db
    finally:
        db.close()

子依赖项 yield

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

您已经看到可以使用依赖项,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会在内部将其转换为上下文管理器,并将其与其他一些相关工具结合起来。

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

警告

这或多或少是一个“高级”的想法。

如果您刚刚开始使用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 将在内部为您完成。


以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号