最初由 Adam Stempniak 和 Daniel Różycki 撰写

创建 REST API 等 Web 应用程序是后端开发人员的生计。因此,使用 Web 框架应该是快速和容易的。

对于需要 REST API(包括 Flask 和 FastAPI)的小型项目、MVP 甚至大型系统来说,微框架是一个很好的开始。

我在这两个框架中编写了一个应用程序来创建、更新、下载和删除新闻。因此,这是我对 FastAPI 和 Flask 的比较。

什么是 Flask?为什么要使用它?

Flask 是用 Python 构建 Web 应用程序的最流行的库之一。从编程开始冒险的人会很容易找到很多 Flask 教程和常见问题的解决方案。

它是轻量级的(一个“微框架”)并且有很好的文档记录,有很多扩展和一个庞大的社区。

什么是 FastAPI?为什么要使用它?

FastAPI 跻身性能最高的 Python Web 框架之列,用于构建 API,并且越来越多地被使用。

它对速度的重视不仅体现在每秒处理的查询数量方面,而且体现在开发速度及其内置数据验证方面,使其成为我们 Web 应用程序后端的理想候选者。

数据验证

在这里,我们可以找到两个库之间的第一个显着差异。

通过安装 Flask,我们没有得到任何数据验证工具。但是,我们可以通过使用社区提供的扩展来解决这个问题,例如Flask-Marshmallow或Flask-Inputs。

这个解决方案的缺点是我们必须依赖与我们的主框架分开开发的库,这意味着我们不能 100% 确定它们会兼容。

另一方面,FastAPI 为我们提供了Pydantic库可供使用,这使得数据验证比手动输入更简单、更快捷。它与 FastAPI 本身密切相关,因此我们可以确定 Pydantic 将始终与我们的框架兼容。

那么,基于我们简单 API 的各个库中的验证是什么?

我们创建名为NewsSchema/CreatorSchema的类,它们将作为验证我们的新闻和作者的基类。

# Flask
@dataclass()
class NewsSchema(BaseSchema):
   title: str = ""
   content: str = ""
   creator: CreatorSchema = CreatorSchema()

@dataclass
class CreatorSchema(BaseSchema):
   first_name: str = ""
   last_name: str = ""

进入全屏模式 退出全屏模式

  # FastAPI
   class NewsSchema(BaseModel):
      title: str = ""
      content: str = ""
      creator: CreatorSchema

   class CreatorSchema(BaseModel):
      first_name: str = ""
      last_name: str = ""

进入全屏模式 退出全屏模式

我们可以注意到 FastAPI 的NewsSchema/CreatorSchema使用BaseModel作为父类。这是必需的,因为BaseModel来自 Pydantic 库,并且具有数据验证所需的功能。

然而,在 Flask 中,我们继承自BaseSchema类,这是一个常规数据类,包含继承类将使用或覆盖的几个方法。

在我们的例子中,我们只会检查我们输入的文本是否在字符限制内。

验证本身将在NewsSchemaInput/CreatorSchemaInput类中进行:

 # Flask
   @dataclass()
   class NewsSchemaInput(NewsSchema):
      _errors: dict = field(init=False, default_factory=dict)

      def _validate_title(self) -> None:
         if MIN_TITLE_LEN > len(self.title) < MAX_TITLE_LEN:
            self._errors[
               "title"
            ] = f"Title should be {MIN_TITLE_LEN}-{MAX_TITLE_LEN} characters long"

      def _validate_content(self) -> None:
         if len(self.content) < MIN_CONTENT_LEN:
            self._errors[
               "content"
            ] = f"Content should be minimum {MIN_CONTENT_LEN} characters long"

      def __post_init__(self) -> None:
         self._validate_content()
         self._validate_title()
         try:
            if not isinstance(self.creator, CreatorSchemaInput):
               self.creator = CreatorSchemaInput(**self.creator)
         except ValidationError as err:
            self._errors["creator"] = err.errors
         if self._errors:
            raise ValidationError(
               f"Validation failed on {type(self).__name__}", self._errors
            )

进入全屏模式 退出全屏模式

# Flask
   @dataclass
   class CreatorSchemaInput(CreatorSchema):
      _errors: dict = field(init=False, default_factory=dict)

      def _validate_first_name(self) -> None:
         if FIRST_NAME_MIN_LEN > len(self.first_name) < FIRST_NAME_MAX_LEN:
            self._errors[
               "first_name"
            ] = f"First name should be {FIRST_NAME_MIN_LEN}-{FIRST_NAME_MAX_LEN} characters long"

      def _validate_last_name(self) -> None:
         if LAST_NAME_MIN_LEN > len(self.last_name) < LAST_NAME_MAX_LEN:
            self._errors[
               "last_name"
            ] = f"Last name should be {LAST_NAME_MIN_LEN}-{LAST_NAME_MAX_LEN} characters long"

      def __post_init__(self) -> None:
         self._validate_first_name()
         self._validate_last_name()
         if self._errors:
            raise ValidationError(
               f"Validation failed on {type(self).__name__}", self._errors
            )

进入全屏模式 退出全屏模式

当我们创建对象NewsSchemaInput/CreatorSchemaInput时,将运行__post_init__方法,在此我们执行数据验证(检查文本长度)。如果不正确,我们将错误添加到_errors变量中,最后引发Validation Error异常。

对于嵌套结构(CreatorSchemaInput),我们必须手动创建这些对象。我们在__post_init__方法中完成NewsSchemaInput验证后执行此操作。

数据检查本身不是什么大问题——只添加新字段会很麻烦,因为我们每次都要添加一个单独的_validate方法。在嵌套结构的情况下,我们必须创建该对象的实例并捕获异常。

我们可以看到验证传入数据的类变得非常广泛——这只是几个键。我们还需要添加自己的错误处理实现,以便我们可以在 API 响应中添加嵌套错误信息。

在 FastAPI 中,它更简单、更有趣:

 # FastAPI
   class NewsSchemaInput(NewsSchema):
      title: str = Field(
         title="Title of the News",
         max_length=MAX_TITLE_LEN,
         min_length=MIN_TITLE_LEN,
         example="Clickbait title",
      )
      content: str = Field(
         title="Content of the News", min_length=50, example="Lorem ipsum..."
      )
      creator: CreatorSchemaInput

进入全屏模式 退出全屏模式

   # FastAPI
   class CreatorSchemaInput(CreatorSchema):
      first_name: str = Field(
         title="First name of the creator",
         min_length=FIRST_NAME_MIN_LEN,
         max_length=FIRST_NAME_MAX_LEN,
         example="John",
      )
      last_name: str = Field(
         title="Last name of the creator",
         min_length=LAST_NAME_MIN_LEN,
         max_length=LAST_NAME_MAX_LEN,
         example="Doe",
      )

进入全屏模式 退出全屏模式

通过从Pydantic导入Field,我们可以访问必须遵循的简单规则,用户输入才有效。数据类型也是基于变量类型进行验证的,因此如果我们的first_name变量具有str类型,我们必须在输入中传递文本(并且对所有内置数据类型采取类似的行为)。

在没有任何额外代码的情况下,Pydantic 可以很好地检查嵌套结构(在本例中为CreatorSchemaInput)。

我们可以在不超过几行代码中找到所有这些!

除了max_lengthmin_length,我们还可以看到另外两个参数:titleexample。它们是可选的,但会在 FastAPI 为我们生成的自动文档中显示。

出站数据序列化

现在我们知道如何验证数据,我们应该考虑如何返回它。

消息不仅包含内容、标题和作者,还包含其唯一编号 (id) 以及创建和更新的日期。我们需要创建一个将序列化News域模型的新类,它将是NewsSchemaOutput

# Flask
   @dataclass
   class NewsSchemaOutput(NewsSchema):
      id: int = 0
      created_at: datetime = datetime.now()
      updated_at: datetime = datetime.now()

      def as_dict(self) -> dict:
         schema_as_dict = super().as_dict()
         schema_as_dict["created_at"] = int(self.created_at.timestamp())
         schema_as_dict["updated_at"] = int(self.updated_at.timestamp())
         return schema_as_dict

进入全屏模式 退出全屏模式

 # FastAPI
   class NewsSchemaOutput(NewsSchema):
      id: int = Field(example="26")
      created_at: datetime = Field(example="1614198897")
      updated_at: datetime = Field(example="1614198897")

      class Config:
         json_encoders = {datetime: lambda dt: int(dt.timestamp())}

进入全屏模式 退出全屏模式

NewsSchemaOutput类在这两种情况下实际上是相同的,唯一的区别是父类和序列化到字典的方法(以及将datetime对象更改为时间戳)。

在 FastAPI 中,使用 Pydantic 时,我们可以选择添加一个Config类,我们在其中放置了json_encoders变量。它有助于以我们需要的方式序列化数据。在这种情况下,我们希望将日期对象作为时间戳传递。然而,在 Flask 中,我们必须将已经创建的字典中的数据更改为我们想要返回的数据。

创建视图和定义数据

在两个库中设置消息非常相似,并且在我们想要使用的函数上使用了一个简单的装饰器。但是,定义数据验证和序列化的方式不同。

 # Flask
   @news_router.route("/news", methods=["POST"])
   def add_news():
      db_repo = get_database_repo()
      news_schema = NewsSchemaInput(**request.get_json())
      news_dto = NewsDTO.from_news_schema(news_schema=news_schema)
      saved_news = db_repo.save_news(news_dto=news_dto)
      output_schema = NewsSchemaOutput.from_entity(news=saved_news).as_dict()
      return output_schema, HTTPStatus.CREATED

进入全屏模式 退出全屏模式

 # FastAPI
   @news_router.post(
      "/news",
      response_model=NewsSchemaOutput,
      summary="Create the news",
      status_code=status.HTTP_201_CREATED,
   )
   async def add_news(
      news_input: NewsSchemaInput,
      db_repo: DatabaseRepository = Depends(get_database_repo),
   ):
      """
      Create the news with following information:

      - **title**: Title of news
      - **content**: News content
      - **creator**: Creator of content
      """
      news_dto = NewsDTO.from_news_schema(news_schema=news_input)
      db_news = await db_repo.save_news(news_dto=news_dto)
      return db_news.as_dict()

进入全屏模式 退出全屏模式

一开始,我们有一个装饰器,它指定要处理的路径和 HTTP 方法。 Flask 使用methods参数设置它,我们需要传递支持的方法列表,而 FastAPI 使用news_router上的post属性。

FastAPI 使用的装饰器不仅用于确定 HTTP 路径和方法,还用于序列化数据(response_model)、描述自动文档中的视图(summary)、定义响应状态(status_code)等等——不是全部其功能的一部分已包含在此示例中。

可以说,FastAPI 不仅定义了访问路径和方法,还深入描述了整个视图。但是这个观点到底发生了什么?让我们从 Flask 开始吧!

我们要做的第一件事是为我们的函数获取数据库存储库: db_repo u003d get_database_repo ()

下一步,我们验证用户提交的数据,这些数据在request对象中:

   db_repo = get_database_repo()

进入全屏模式 退出全屏模式

   news_schema = NewsSchemaInput(**request.get_json())

进入全屏模式 退出全屏模式

如果输入无效,此行将引发ValidationError异常。

异常将在我们创建的errorhandler中捕获,Flask 将返回一个回复,其中包含NewsSchemaInput上的_errors变量中的所有错误。

但请稍等!我们还没有讨论我们应该创建的errorhandler

在 Flask 和 FastAPI 中,我们可以添加自己的异常处理,这将在视图实现中抛出。它们看起来像这样:

 # Flask
   @app.errorhandler(ValidationError)
   def handle_validation_error(exc: ValidationError) -> Tuple[dict, int]:
      status_code = HTTPStatus.UNPROCESSABLE_ENTITY
      return {"detail": exc.errors}, status_code

进入全屏模式 退出全屏模式

# FastAPI
   @app.exception_handler(ValidationError)
   async def handle_validation_error(request: Request, exc: ValidationError):
      return JSONResponse(
         status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
         content={"detail": exc.errors()},
      )

进入全屏模式 退出全屏模式

如果验证成功,则创建一个NewsDTO对象,它将必要的信息传递到数据库存储库。存储库将发挥作用(在数据库中保存一条消息)并将News域对象返回给我们,然后我们使用NewsSchemaOutput类对其进行序列化:

 news_dto = NewsDTO.from_news_schema(news_schema=news_schema)
   saved_news = db_repo.save_news(news_dto=news_dto)
   output_schema = NewsSchemaOutput.from_entity(news=saved_news).as_dict()

进入全屏模式 退出全屏模式

最后,我们返回NewsSchemaOutput作为字典和响应状态:

   return output_schema, HTTPStatus.CREATED

进入全屏模式 退出全屏模式

现在,让我们看一下 FastAPI。这一次,我们在视图中得到了两个参数:news_inputdb_repo

在第一个中,输入数据验证发生在我们的视图方法执行之前,这要归功于news_input参数。

您可能会问自己:FastAPI 如何知道要使用哪个类?这要归功于打字。news_input参数具有NewsSchemaInput类型,因此 FastAPI 所做的是将所有数据传递给我们使用 POST 方法发送的此类。我们不需要创建NewsSchemaInput对象的实例,因为我们将在news_input参数中获得经过验证的数据。

关于db_repo,它的工作方式与 Flask 类似,只是这里我们使用了依赖注入。Depends关键字允许您在我们的应用程序运行时替换类或函数。稍后我们将讨论dependency injection

   async def add_news(
      news_input: NewsSchemaInput,
      db_repo: DatabaseRepository = Depends(get_database_repo),
   ):

进入全屏模式 退出全屏模式

当我们的方法被调用时,我们将消息保存在数据库中。

   db_news = await db_repo.save_news(news_dto=news_dto)

进入全屏模式 退出全屏模式

在 Flask 中,我们必须创建一个NewsSchemaOutput类的实例来返回正确的数据。与响应状态相同:它也使用return关键字返回。

FastAPI 允许您使用装饰器中的response_model参数指定一个类来序列化数据。我们需要做的就是提供Pydatnic能够理解的正确结构。响应状态也可以和response_model在同一个地方设置,但是使用status_code参数。

获取消息,地址中的变量,GET参数

就像我们在创建帖子时一样,我们使用简单的装饰器定义视图。然而,这一次,我们使用 GET 方法。

   # Flask
   @news_router.route("/news/<int:news_id>", methods=["GET"])
   def get_news(news_id: int):
      db_repo = get_database_repo()
      news_from_db = db_repo.get_news(news_id=news_id)
      output_schema = NewsSchemaOutput.from_entity(news=news_from_db).as_dict()
      return output_schema

进入全屏模式 退出全屏模式

   # FastAPI
   @router.get(
      "/news/{news_id}",
      response_model=NewsSchemaOutput,
      summary="Get the news by ID",
      responses=NOT_FOUND_FOR_ID,
   )
   async def get_news(
      news_id: int, db_repo: DatabaseRepository = Depends(get_database_repo)
   ):
      """
      Get the news with passed ID
      """
      db_news = await db_repo.get_news(news_id=news_id)
      return db_news.as_dict()

进入全屏模式 退出全屏模式

要下载我们感兴趣的消息,我们需要将其 id 传递给我们的视图。我们使用添加了news_id参数的地址来执行此操作。在 Flask 中,我们必须使用尖括号和名称详细指定其类型,即<int: news_id>。我们被迫只使用 Flask 可以理解的基本类型,例如 int、uuid、str 或 float 等。

FastAPI 使用类似于 f-string 的约定,其中变量的名称由大括号定义,其类型在视图函数的参数中设置。

这是一个更灵活的解决方案,因为我们可以尝试在地址中传递复杂的结构。您可能还注意到视图装饰器中出现了一个新参数。这个参数叫做responses——我们会在讨论自动文档时回到它。

过滤带有GET参数的消息

当我们想要一个灵活的解决方案时,我们不是创建需要在地址中定义变量的视图,而是使用 GET 参数。在这种情况下,我们需要返回符合所谓query parameters传递给我们的条件的消息。我们有两个参数:idcreated_at

   # Flask
   @news_router.route("/news", methods=["GET"])
   def get_news_by_filter():
      db_repo = get_database_repo()
      ids = request.args.getlist("id", type=int)
      created_at = request.args.getlist("created_at", type=int)
      news_from_db = db_repo.get_news_by_filter(id=ids, created_at=created_at)
      return jsonify(
         [NewsSchemaOutput.from_entity(news=news).as_dict() for news in news_from_db]
      )

进入全屏模式 退出全屏模式

   # FastAPI
   @router.get(
      "/news",
      response_model=List[NewsSchemaOutput],
      summary="Get the news by filter",
      responses=NOT_FOUND_FOR_ID,
   )
   async def get_news_by_filter(
      id: Set[int] = Query(set()),
      created_at: Set[datetime] = Query(set()),
      db_repo: DatabaseRepository = Depends(get_database_repo),
   ):
      """
      Get the news with passed filters.

      - **id**: List of id to search for
      - **created_at**: List of date of creation timestamps
      """
      db_news = await db_repo.get_news_by_filter(id=id, created_at=created_at)
      return [news.as_dict() for news in db_news]

进入全屏模式 退出全屏模式

Flask 提供了 request 对象,我们可以从中提取有关请求的数据到我们的视图方法。 Flask 提供了一个request对象,我们可以从中检索所有查询数据到我们的视图。

这一次,我们对idcreated_at参数感兴趣。我们还知道我们可以期待这些参数的列表——为此,我们使用特殊的args字典中的getlist方法。

   ids = request.args.getlist("id", type=int)
   created_at = request.args.getlist("created_at", type=int)

进入全屏模式 退出全屏模式

然后我们将提取的数据发送到数据库存储库以获取News域模型列表,我们将其转换为NewsSchemaOutput类的字典列表。

   news_from_db = db_repo.get_news_by_filter(id=ids, created_at=created_at)
   [NewsSchemaOutput.from_entity(news=news).as_dict() for news in news_from_db]

进入全屏模式 退出全屏模式

我们还必须记住,我们不能从视图中返回列表——必须为端点执行jsonify函数以返回具有正确序列化列表的Response对象。

   return jsonify(
         [NewsSchemaOutput.from_entity(news=news).as_dict() for news in news_from_db]
      )

进入全屏模式 退出全屏模式

使用 FastAPI,整个过程看起来与 Flask 非常相似——不同之处在于我们在函数参数中获取地址变量,这比对我们需要的每个变量执行request.args.getlist更具可读性。为了让 FastAPI 知道函数参数是地址变量,我们需要给它们添加默认的Query值,这是预定义的。

如果我们没有在花括号中指定特定的数据类型,FastAPI 怎么知道我们想要一个特定的数据类型?打字显示它。

我们需要做的就是为我们的参数添加一个类型,例如set [int],我们将确保该变量将包含一个仅包含整数的集合。

验证地址变量后,我们使用发送的标准从数据库存储库中提取News域模型。然后我们返回消息模型字典列表,装饰器中的response_model将处理数据的正确序列化。

   db_news = await db_repo.get_news_by_filter(id=id, created_at=created_at)
      return [news.as_dict() for news in db_news]

进入全屏模式 退出全屏模式

依赖注入

依赖注入是设计和软件架构中的一种模式,它基于消除组件之间的直接依赖关系。

听起来很复杂,对吧?好吧,FastAPI 能够以非常简单的方式实现这种模式。

我们可能已经注意到,在每个视图中,函数参数中都有这样的东西:

   db_repo: DatabaseRepository = Depends(get_database_repo)

进入全屏模式 退出全屏模式

这就是我们所说的依赖注入——在这种情况下,我们正在注入数据库存储库。Depends关键字能够注入任何可以命名的东西(例如类或函数)。这是一个很好的方法,因为它允许您遵守 DRY(不要重复自己)规则,因为您不必每次都为数据库存储库创建一个新变量,就像在 Flask 中所做的那样:

   db_repo = get_database_repo()

进入全屏模式 退出全屏模式

Depends的另一个优点是它可以很容易地替换测试中的实现。

在 Flask 中,要替换get_database_repo的返回值,我们必须在每次运行测试时模拟这个函数。

   @mock.patch("path.to.dependency.get_database_repo)
   def test_some_view(db_repo_inject_mock):
      db_repo_inject_mock.return_value = OUR OWN DB REPO IMPLEMENTATION

进入全屏模式 退出全屏模式

感谢 FastAPI 中的依赖注入。我们可以用...

   app.dependency_overrides[db_repo] = OUR OWN CALLABLE IMPLEMENTATION

进入全屏模式 退出全屏模式

...在运行测试时替换实现。

Depends也可以用来不重复相同的函数参数n次。有关更多信息,请查看文档。

异步性

不幸的是,Flask 不支持异步和 ASGI 接口,这意味着一些长时间运行的查询可能会阻塞我们的应用程序。这与我们可以使用 REST API 处理的用户数量较少有关。

您可能已经注意到,FastAPI 中的视图函数以async开头,并且在数据库存储库上调用的每个方法都以单词await开头。

FastAPI 是完全异步的——这并不意味着它是必需的,因为我们也可以实现普通的同步函数——并且使用 ASGI 接口。多亏了这一点,我们可以对数据库或外部服务使用非阻塞查询,这意味着同时使用我们的应用程序的用户数量将比使用 Flask 的情况大得多。

在它的文档中,FastAPI 有一个非常好的使用asyncawait的示例。我强烈推荐阅读它!

那么运行基准测试呢?

对于这个任务,我们将使用Locust。它是一个免费的开源 Python 负载测试工具。我们的测试将基于每秒向活动连接池中添加 100 个用户,直到我们同时达到 2,000 个用户。

[Locust - Flask](https://res.cloudinary.com/practicaldev/image/fetch/s--aG3oVniT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to -uploads.s3.amazonaws.com/uploads/articles/petgvdpbv76inqroag2n.png) Flask

如我们所见,我们每秒可以处理的查询数约为 633。这还不错,对吧?不过,它可能会更好。响应的平均等待时间约为 1,642 毫秒——实际上,从 API 接收任何数据需要 1.5 秒,这绝对是太多了。为此,我们可以添加 7% 的不成功查询。

[Locus - FastAPI](https://res.cloudinary.com/practicaldev/image/fetch/s--lBWxxIrz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to -uploads.s3.amazonaws.com/uploads/articles/zc4sqvl6vq9skqggvbre.png)FastAPI

FastAPI 在这项任务中做得更好。我们可以处理的查询数量约为每秒 1,150 个(几乎是 Flask 中的两倍),响应的平均等待时间仅为...... 14 ms。所有查询都是正确的,我们没有发现任何错误。

自动建档

在创建 REST API 时,文档对于希望使用此接口与我们的应用程序进行通信的开发人员或用户团队来说是必不可少的。

您可以手动完成,例如在 Jira Confluence / Github wiki 或任何其他设计数据收集工具中。但是,存在人为错误的风险,例如当有人忘记将地址更新为视图或打错字时。

创建此类文档的最常见标准是OpenAPI和JSONSchema。

Flask 提供了扩展,例如Flask-Swagger或Flasgger,它们使用上述规范进行操作。它们需要额外的安装和这些标准使用的格式的知识。

此外,传输数据的规范必须手动保存——它们不会从验证的类或我们下载的参数中获取。

FastAPI 具有与 OpenAPI 和 JSONSchema 完全兼容的文档,这些文档是从 Pydantic 模式和函数参数或 GET 变量自动创建的。用户界面由SwaggerUI和Redoc提供。

这是一个非常有趣的功能,因为它不需要我们做任何工作(除非我们想用细节来美化我们的文档)。所需数据的所有规则都可以在 Pydatnic 模式中找到。

文档位于host / doc(SwaggerUI) 和host / redoc(ReDoc),如下所示:

[自动文档 - SwaggerUI - FastAPI](https://res.cloudinary.com/practicaldev/image/fetch/s--xD0Hhz-I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https: //dev-to-uploads.s3.amazonaws.com/uploads/articles/yomj2qi94opvj99i6w8x.png)Swagger UI

[自动文档 - SwaggerUI - ReDoc](https://res.cloudinary.com/practicaldev/image/fetch/s--y7L8eTz1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https:// dev-to-uploads.s3.amazonaws.com/uploads/articles/6pefe6m3aqrkqzcci2h1.png)

在 SwaggerUI 中,我们还可以访问我们在应用程序中定义的所有模式:

[架构](https://res.cloudinary.com/practicaldev/image/fetch/s--_Cr3gMXF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads .s3.amazonaws.com/uploads/articles/u1tbw5rtartepiay6p1o.png)

我们可以注意到出现了来自CreatorSchemaInputsummarytitle参数的信息。

FastAPI 如何知道将哪些信息传递给文档?我们来看一个下载消息的例子:

   # FastAPI
   @router.get(
      "/news/{news_id}",
      response_model=NewsSchemaOutput,
      summary="Get the news by ID",
      responses=NOT_FOUND_FOR_ID,
   )
   async def get_news(
      news_id: int, db_repo: DatabaseRepository = Depends(get_database_repo)
   ):
      """
      Get the news with passed ID
      """
      db_news = await db_repo.get_news(news_id=news_id)
      return db_news.as_dict()

进入全屏模式 退出全屏模式

在创建文档时会考虑装饰器中的一些参数:

  • / news / {news_id}——在文档中,我们会看到news_id参数是必需的,必须是整数

  • response_model——这个响应方案会自动显示在文档中

  • responses——如果我们的视图返回的响应代码不是 200/400/422 或 500,我们可以添加一个带有状态和返回数据模式的特殊字典,如下所示:

   NOT_FOUND_FOR_ID: Response_Type = {
      404: {
         "description": "News with given ID wasn't found",
         "content": {
            "application/json": {"example": {"detail": "News with id {id} don't exist"}}
         },
      }
   }

进入全屏模式 退出全屏模式

此外,文档字符串也被考虑在内,并将显示为特定视图的附加信息。

[自动文档 - 文档字符串](https://res.cloudinary.com/practicaldev/image/fetch/s--H5agx5-Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https:// dev-to-uploads.s3.amazonaws.com/uploads/articles/khgz63pq4esb5fekzx5d.png)

关于 Flask 和 FastAPI 的最终想法

感谢您阅读我使用来自 REST API 的一个非常简单的 CRUD 应用程序作为示例对这两个优秀库的比较。

一方面,我们有非常流行的Flask,不容忽视;另一方面是 FastAPI,它以内置功能的数量和异步性赢得了用户的心。

那么,哪一个更好呢?就个人而言,如果我要为我的下一个 REST 项目选择框架,我肯定会倾向于 FastAPI。

当然,您可以自由得出自己的结论并做出不同的选择。但是,我希望您至少尝试给 FastAPI 一个机会。

Logo

学AI,认准AI Studio!GPU算力,限时免费领,邀请好友解锁更多惊喜福利 >>>

更多推荐