使用 FastAPI 构建 Python Web API

目录

创建 API 或应用程序编程接口是让广大用户可以访问您的软件的重要部分。在本教程中,您将学习FastAPI的主要概念以及如何使用它来快速创建默认实现最佳实践的 Web API。

到最后,您将能够开始创建生产就绪的 Web API,并且您将获得深入了解所需的理解,并针对您的特定用例了解更多信息。

在本教程中,您将学习如何

  • 使用路径参数获取每个项目的唯一 URL 路径

  • 使用pydantic在您的请求中接收 JSON 数据

  • 使用 API 最佳实践,包括验证序列化文档

  • 继续为您的用例学习 FastAPI

本教程由FastAPI作者编写。它包含从官方文档中精心挑选的片段,避免迷失在技术细节中,同时帮助您尽快上手。

为了充分利用本教程,了解什么是 HTTP 及其工作原理什么是 JSON以及Python 类型提示的基础知识会对您有所帮助。您还将受益于使用虚拟环境,就像任何 Python 项目一样。

什么是 FastAPI?

FastAPI 是一个现代的、高性能的 Web 框架,用于基于标准类型提示使用 Python 构建 API。它具有以下主要特点:

  • 运行速度快:它提供了非常高的性能,看齐的NodeJS围棋,感谢Starlettepydantic

  • 快速编码:它允许显着提高开发速度。

  • 减少错误数量:它减少了人为错误的可能性。

  • 直观:它提供了强大的编辑器支持,随处完成,调试时间更少。

  • 直截了当:它的设计易于使用和学习,因此您可以花更少的时间阅读文档。

  • 简短:它最大限度地减少了代码重复。

  • 健壮:它提供带有自动交互式文档的生产就绪代码。

  • 基于标准:它基于 API、OpenAPIJSON Schema的开放标准。

该框架旨在优化您的开发人员体验,以便您可以编写简单的代码来默认使用最佳实践构建生产就绪的 API。

安装 FastAPI

与任何其他 Python 项目一样,最好从创建虚拟环境开始。如果您不熟悉如何做到这一点,那么您可以查看虚拟环境入门

第一步是使用以下命令安装 FastAPI 和Uvicornpip

$ python -m pip install fastapi uvicorn[standard]

这样,您就安装了 FastAPI 和 Uvicorn,并准备好学习如何使用它们。FastAPI 是您将用于构建 API 的框架,而 Uvicorn 是将使用您构建的 API 来处理请求的服务器。

第一步

首先,在本节中,您将创建一个最小的 FastAPI 应用程序,使用 Uvicorn 在服务器上运行它,然后学习所有交互部分。这将使您非常快速地了解一切是如何工作的。

创建第一个 API

一个基本的 FastAPI 文件如下所示:

# main.py

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

将上面的代码复制到名为 的文件中main.py,这样,您就有了一个功能齐全的 API 应用程序,其中包含一些最佳实践,例如内置的自动文档和序列化。接下来您将了解有关这些功能的更多信息。

此代码定义了您的应用程序,但如果您python直接调用它,它不会自行运行。要运行它,您需要一个服务器程序。在上述步骤中,您已经安装了Uvicorn。那将是您的服务器。

使用 Uvicorn 运行第一个 API 应用程序

使用 Uvicorn 运行实时服务器:

$ uvicorn main:app --reload

INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [28720]
INFO:     Started server process [28722]
INFO:     Waiting for application startup.
INFO:     Application startup complete.

输出中突出显示的行显示了在本地计算机中提供应用程序的 URL。由于您用于--reload开发,当您更新应用程序代码时,服务器会自动重新加载。

检查响应

打开您的浏览器到http://127.0.0.1:8000,这将使您的浏览器向您的应用程序发送请求。然后它会发送一个带有以下内容的 JSON 响应:

{"message": "Hello World"}

该 JSON 消息与您从应用程序中的函数返回的字典相同。FastAPI 负责将Pythondict序列化为 JSON 对象并设置适当的Content-Type.

检查交互式 API 文档

现在http://127.0.0.1:8000/docs在浏览器中打开。

您将看到Swagger UI提供的自动交互 API 文档:

Swagger UI 提供的交互式 API 文档

默认情况下提供并集成了用于记录 API 的基于 Web 的用户界面。您无需执行任何其他操作即可通过 FastAPI 来利用它。

检查替代交互式 API 文档

现在,http://127.0.0.1:8000/redoc在浏览器中转到。

您将看到ReDoc提供的替代自动文档:

ReDoc 提供的交互式 API 文档


由于 FastAPI 基于 OpenAPI 等标准,因此有多种显示 API 文档的替代方法。FastAPI 默认提供这两种选择。

第一个 API,循序渐进

现在让我们逐步分析该代码并了解每个部分的作用。

第 1 步是导入FastAPI

# main.py

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

FastAPI是一个Python 类,可为您的 API 提供所有功能。

第二步是创建一个FastAPI实例:

# main.py

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

这里的app 变量将是类的一个实例FastAPI。这将是创建 API 的主要交互点。

app与您在上面使用以下命令运行实时服务器的命令中提到的相同uvicorn

$ uvicorn main:app --reload

INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

在继续第 3 步之前,值得花点时间熟悉几个术语。路径是指从第一个正斜杠字符 ( /)开始的 URL 的最后一部分。因此,在像 的 URL 中https://example.com/items/foo,路径将是/items/foo

路径通常也称为端点路由,但本教程中将使用术语路径。在构建 API 时,路径是分离资源的主要方式。

另一个需要了解的重要术语是operation,它用于指代任何HTTP 请求方法

  • POST

  • GET

  • PUT

  • DELETE

  • OPTIONS

  • HEAD

  • PATCH

  • TRACE

通过HTTP,您可以使用这些操作中的一个(或多个)与每个路径进行通信。了解这两个术语的含义后,您就可以继续执行第三步了。

第 3 步是定义一个路径操作装饰器

# main.py

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

@app.get("/")告诉FastAPI是正下方的功能是负责处理那些对路径请求/使用get操作。这是一个与路径操作相关的装饰器,或者说是一个路径操作装饰器。如果您想了解更多关于装饰器的知识,请查看Python 装饰器入门

您还可以使用上面提到的其他操作:

  • @app.post()

  • @app.put()

  • @app.delete()

  • @app.options()

  • @app.head()

  • @app.patch()

  • @app.trace()

在每种情况下,您都可以在负责处理这些请求的函数上方使用适当的路径操作装饰器。

提示:您可以随意使用每个操作(HTTP 方法)。

FastAPI 不强制执行任何特定含义。此处提供的信息仅供参考,而不是要求。

例如,在使用 GraphQL 时,您通常只使用POST操作执行大部分操作

第4步是定义路径操作函数,或者路径操作装饰器下面的函数:

# main.py

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

每当 FastAPI/使用GET操作接收到对指定 URL ( )的请求时,就会调用此函数。在这种情况下,它是一个async函数

您也可以将其定义为普通函数而不是使用async def

# main.py

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def root():
    return {"message": "Hello World"}

如果您不知道普通函数和async函数之间的区别以及何时使用它们,请查看FastAPI 文档中的Concurrency 和 async/await

第五步返回内容:

# main.py

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

您可以将字典列表或奇异值作为字符串整数等返回。您还可以返回 pydantic 模型,稍后您将了解更多信息。

还有许多其他对象和模型将自动转换为 JSON,包括对象关系映射器(ORM) 等。尝试使用您最喜欢的 - 它们很可能已经得到支持。

路径参数:通过 ID 获取项目

您可以使用 Python格式化字符串使用的相同语法声明路径参数变量

# main.py

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(item_id):
    return {"item_id": item_id}

路径参数的值item_id将作为参数传递给您的函数item_id

因此,如果您运行此示例并转到http://127.0.0.1:8000/items/foo,您将看到以下响应:

{"item_id":"foo"}

响应包含"foo",这是在item_id路径参数中传递然后在字典中返回的内容。

带类型的路径参数

您可以使用标准 Python 类型提示在函数中声明路径参数的类型:

# main.py

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(item_id: int):
    return {"item_id": item_id}

在这种情况下,您声明item_idint.

声明路径参数的类型将为您的函数内部提供编辑器支持,包括错误检查、完成等。

数据转换

如果您运行上面的示例并将浏览器导航到http://127.0.0.1:8000/items/3,那么您将看到以下响应:

{"item_id":3}

请注意,您的函数接收并返回的值是3,它是一个 Python int,而不是字符串 ( "3")。因此,通过该类型声明,FastAPI 为您提供自动请求解析

数据验证

如果您将浏览器指向http://127.0.0.1:8000/items/foo,则会看到一个不错的 HTTP 错误:

{
    "detail": [
        {
            "loc": [
                "path",
                "item_id"
            ],
            "msg": "value is not a valid integer",
            "type": "type_error.integer"
        }
    ]
}

这是因为 path 参数item_id的值是"foo",而不是int

如果您提供的是float而不是 ,则会出现相同的错误int,例如http://127.0.0.1:8000/items/4.2在浏览器中打开时。因此,使用相同的 Python 类型提示,FastAPI 为您提供数据解析数据验证

另请注意,该错误清楚地指出了验证未通过的确切点。这在开发和调试与 API 交互的代码时非常有用。

文档

当您在 处打开浏览器时http://127.0.0.1:8000/docs,您将看到一个自动的交互式 API 文档:

用于路径操作的交互式 API 文档

同样,通过相同的 Python 类型声明,FastAPI 为您提供了集成 Swagger UI 的自动交互式文档。请注意,path 参数被声明为一个整数。

由于 FastAPI 构建在OpenAPI标准之上,因此它还提供了使用 ReDoc 的替代 API 文档,您可以在http://127.0.0.1:8000/redoc以下位置访问:

使用 ReDoc 的替代交互式 API 文档

还有许多其他兼容工具,包括用于多种语言的代码生成工具。

使用 pydantic 处理数据

所有数据验证都由 pydantic 在后台执行,因此您可以从中获得所有好处,并且您知道自己处于良好状态。

您可以使用同一类型的声明有strfloatbool和许多其他复杂数据类型。

订单问题:将固定路径放在首位

创建路径操作时,您可能会发现有固定路径的情况,例如/users/me. 假设它是获取有关当前用户的数据。您可能还有/users/{user_id}通过某个用户 ID 获取有关特定用户的数据的路径。

因为路径操作是按顺序计算的,所以您需要确保路径 for/users/me在 for 之前声明/users/{user_id}

# main.py

from fastapi import FastAPI

app = FastAPI()

@app.get("/users/me")
async def read_user_me():
    return {"user_id": "the current user"}

@app.get("/users/{user_id}")
async def read_user(user_id: str):
    return {"user_id": user_id}

否则,路径 for/users/{user_id}也将匹配 for /users/me,认为它正在接收user_id值为的参数"me"

请求正文:接收 JSON 数据

当您需要将数据从客户端发送到您的 API 时,您可以将其作为请求正文发送。

一个请求体是由客户端发送到您的API数据。一个响应的身体是你的API发送到客户端的数据。您的 API 几乎总是必须发送响应正文。但是客户端不一定需要一直发送请求正文。

注意:要发送数据,您应该使用POST(最常见的方法)PUTDELETE、 或PATCH。发送带有GET请求的正文在规范中具有未定义的行为。

尽管如此,GETFastAPI 支持使用请求,但仅适用于非常复杂或极端的用例。由于不鼓励,使用 Swagger UI 的交互式文档在使用时不会显示正文的文档GET,并且中间的代理可能不支持它。

要声明请求正文,您可以使用 pydantic 模型,以及它们的所有功能和优点。您将在下面了解有关它们的更多信息。

使用 pydantic 声明 JSON 数据模型(数据形状)

首先,您需要BaseModel从 from导入pydantic,然后使用它来创建定义要接收的架构或数据形状的子类。

接下来,将数据模型声明为继承自 的类,BaseModel对所有属性使用标准 Python 类型:

# main.py

from typing import Optional

from fastapi import FastAPI
from pydantic import BaseModel

class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: Optional[float] = None

app = FastAPI()

@app.post("/items/")
async def create_item(item: Item):
    return item

当模型属性具有默认值时,它不是必需的。否则,它是必需的。要使属性可选,您可以使用None.

例如,上面的模型声明了一个 JSON 对象(或 Python dict),如下所示:

{
    "name": "Foo",
    "description": "An optional description",
    "price": 45.2,
    "tax": 3.5
}

在这种情况下,由于descriptiontax是可选的,因为它们的默认值为None,因此此 JSON 对象也将有效:

{
    "name": "Foo",
    "price": 45.2
}

省略默认值的 JSON 对象也是有效的。

接下来,将新的 pydantic 模型作为参数添加到您的路径操作中。您以与声明路径参数相同的方式声明它:

# main.py

from typing import Optional

from fastapi import FastAPI
from pydantic import BaseModel

class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: Optional[float] = None

app = FastAPI()

@app.post("/items/")
async def create_item(item: Item):
    return item

该参数item的类型提示为Item,这意味着它item被声明为类的实例Item

使用该 Python 类型声明,FastAPI 将:

  • 将请求正文读取为 JSON

  • 如果需要,转换相应的类型

  • 验证数据,如果无效则返回一个明确的错误

  • 在参数中item为您提供接收到的数据——因为您将其声明为 type Item,您还将获得所有编辑器支持,以及对所有属性及其类型的完成和类型检查

  • 为您的模型生成JSON Schema定义,您也可以在对您的项目有意义的任何其他地方使用它

通过在 pydantic 中使用标准类型提示,FastAPI 可以帮助您轻松构建默认情况下具有所有这些最佳实践的 API。

使用 pydantic 自动记录

pydantic 模型的 JSON 模式将是为您的应用程序生成的 OpenAPI 的一部分,并将显示在交互式 API 文档中:

pydantic 模型的 JSON 模式包含在 API 文档 UI 中

您可以看到ItemAPI 文档中的属性正是您使用 pydantic 模型声明的属性。

这些 JSON 模式也将用于需要它们的每个路径操作内的 API 文档中:

pydantic 模型中的 JSON Schema 包含在 API 文档 UI 中的每个路径操作中

请注意,所有这些自动文档都是基于您的数据,使用您的 pydantic 模型。

编辑器支持、自动完成和类型检查

在您的编辑器中,在您的函数中,您将在任何地方获得类型提示和完成。如果您收到的是dict而不是 pydantic 模型,则不会发生这种情况:

编辑器为 pydantic 模型提供完成和类型检查

通过这种方式,您可以为所有数据触发自动完成。

您还会对不正确的类型操作进行错误检查:

编辑器为 pydantic 模型提供错误检查

在这种情况下,您不能将 astr与 a相加float,因为编辑器知道这些类型,所以它可以警告您代码中有错误。这并非偶然:整个框架都是围绕该设计构建的。它在任何实施之前的设计阶段都经过彻底测试,以确保它适用于所有编辑器。甚至对 pydantic 本身进行了一些更改以支持此功能。

之前的屏幕截图是使用Visual Studio Code拍摄的。但是您将获得与PyCharm和大多数其他 Python 编辑器相同的编辑器支持:

PyCharm 还提供编辑器支持,如 VS Code

如果您使用 PyCharm 作为您的编辑器,那么您可以使用pydantic PyCharm 插件来改进您的编辑器支持。如果您使用 VS Code,那么您将获得使用Pylance的最佳开发人员体验。

使用pydantic模型

在函数内部,您可以直接访问模型对象的所有属性:

# main.py

from typing import Optional

from fastapi import FastAPI
from pydantic import BaseModel

class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: Optional[float] = None

app = FastAPI()

@app.post("/items/")
async def create_item(item: Item):
    item_dict = item.dict()
    if item.tax:
        price_with_tax = item.price + item.tax
        item_dict.update({"price_with_tax": price_with_tax})
    return item_dict

该参数item被声明为 class 的一个实例Item,FastAPI 将确保您在您的函数中接收到它,而不是字典或其他东西。

请求正文和路径参数

您可以同时声明路径参数和请求正文。

FastAPI 会识别匹配路径参数的函数参数应该从路径中获取,声明为pydantic模型的函数参数应该从请求体中获取:

# main.py

from typing import Optional

from fastapi import FastAPI
from pydantic import BaseModel

class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: Optional[float] = None

app = FastAPI()

@app.put("/items/{item_id}")
async def create_item(item_id: int, item: Item):
    return {"item_id": item_id, **item.dict()}

通过这种方式,您可以声明路径参数和 JSON 请求正文,FastAPI 将负责为您完成所有数据验证、序列化和文档处理。您可以通过访问相同的 API 文档/docs或使用其他工具(如带有图形界面的Postman或命令行中的Curl)来验证它。

以类似的方式,您可以声明更复杂的请求主体(如列表)和其他类型的请求数据,如查询参数、cookie、标头、表单输入、文件等。

了解有关 FastAPI 的更多信息

到目前为止,您已经对 FastAPI 以及如何使用它来创建强大且生产就绪的 API 了如指掌。

但是你可以学到更多:

FastAPI 可以涵盖后端框架所需的大多数用例,即使是那些不是严格意义上的 API 的用例。您可以深入了解文档以解决您的特定用例。

FastAPI 基于现代 Python 功能,您也可以通过详细了解这些功能来充分利用 FastAPI。看看入门异步特性在Python异步IO在Python:一个完整的演练,详细了解asyncronous编程。您还可以查看Python 类型检查(指南)以从代码中的类型提示中获得所有传统优势。

结论

在本教程中,您了解了FastAPI以及如何使用它来创建生产就绪的 API,这些 API 在默认情况下具有最佳实践,同时提供尽可能最佳的开发人员体验。你学会了如何:

  • 使用路径参数获取每个项目的唯一 URL 路径

  • 使用pydantic在您的请求中接收 JSON 数据

  • 使用 API 最佳实践,如验证序列化文档

  • 继续学习不同用例的FastAPIpydantic

您现在已准备好开始为您的项目创建自己的高性能 API。如果您想深入了解 FastAPI 的世界,那么您可以按照FastAPI 文档中的官方用户指南进行操作。


(完)