跳到内容

路径参数

你可以使用与 Python 格式化字符串相同的语法来声明路径“参数”或“变量”。

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"}

带类型的路径参数

你可以使用标准 Python 类型注解在函数中声明路径参数的类型。

from fastapi import FastAPI

app = FastAPI()


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

在此例中,item_id 被声明为 int 类型。

提示

这将为你提供函数内部的编辑器支持,包括错误检查、补全等功能。

数据 转换

如果你运行此示例并打开浏览器访问 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": [
    {
      "type": "int_parsing",
      "loc": [
        "path",
        "item_id"
      ],
      "msg": "Input should be a valid integer, unable to parse string as an integer",
      "input": "foo"
    }
  ]
}

因为路径参数 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 文档,如下所示:

提示

同样,只需相同的 Python 类型声明,FastAPI 就能为你提供自动化的交互式文档(集成了 Swagger UI)。

请注意,该路径参数被声明为整数。

基于标准的优势,替代性文档

由于生成的模式基于 OpenAPI 标准,因此有许多兼容工具可用。

正因如此,FastAPI 本身提供了另一种 API 文档(使用 ReDoc),你可以通过 http://127.0.0.1:8000/redoc 访问。

同样,有许多兼容工具。包括针对多种语言的代码生成工具。

Pydantic

所有数据验证均由 Pydantic 在后台执行,因此你可以从中获得所有好处。你可以放心使用。

你可以对 strfloatbool 以及许多其他复杂数据类型使用相同的类型声明。

教程的后续章节将探索其中一些类型。

顺序很重要

在创建 路径操作 时,可能会遇到固定路径的情况。

例如 /users/me,假设它是为了获取当前用户的数据。

然后你还可以有一个路径 /users/{user_id},用于通过用户 ID 获取特定用户的数据。

因为 路径操作 是按顺序评估的,你需要确保 /users/me 的路径声明在 /users/{user_id} 之前。

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}

否则,/users/{user_id} 的路径也会匹配 /users/me,“认为”它接收到了一个值为 "me" 的参数 user_id

同样,你不能重复定义同一个路径操作。

from fastapi import FastAPI

app = FastAPI()


@app.get("/users")
async def read_users():
    return ["Rick", "Morty"]


@app.get("/users")
async def read_users2():
    return ["Bean", "Elfo"]

因为路径首先匹配,所以总是会使用第一个定义的路径操作。

预定义值

如果你有一个接收 路径参数路径操作,但你希望 路径参数 的有效值是预定义的,你可以使用标准的 Python Enum

创建一个 Enum

导入 Enum 并创建一个继承自 strEnum 的子类。

通过继承自 str,API 文档将能够知道这些值必须是 string 类型,并能正确渲染。

然后创建具有固定值的类属性,这些即为可用的有效值。

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name is ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

提示

如果你在好奇,“AlexNet”、“ResNet”和“LeNet”只是机器学习 模型 的名称。

声明一个 路径参数

然后使用你创建的枚举类 (ModelName) 作为类型注解来创建一个 路径参数

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name is ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

检查文档

因为 路径参数 的可用值是预定义的,交互式文档可以很好地展示它们。

使用 Python 枚举

路径参数 的值将是一个 枚举成员

比较 枚举成员

你可以将其与你创建的枚举 ModelName 中的 枚举成员 进行比较。

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name is ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

获取 枚举值

你可以使用 model_name.value,或者更通用的 your_enum_member.value 来获取实际值(在此例中为 str)。

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name is ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

提示

你也可以通过 ModelName.lenet.value 访问值 "lenet"

返回 枚举成员

你可以从 路径操作 中返回 枚举成员,即使是嵌套在 JSON 正文中(例如 dict)。

在返回给客户端之前,它们会被转换为相应的值(在此例中为字符串)。

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name is ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

在你的客户端中,你将收到如下 JSON 响应:

{
  "model_name": "alexnet",
  "message": "Deep Learning FTW!"
}

包含路径的路径参数

假设你有一个路径为 /files/{file_path}路径操作

但你需要 file_path 本身包含一个 路径,例如 home/johndoe/myfile.txt

那么该文件的 URL 将类似于:/files/home/johndoe/myfile.txt

OpenAPI 支持

OpenAPI 不支持声明 路径参数 包含内部 路径 的方式,因为这可能导致难以测试和定义的场景。

尽管如此,你仍然可以在 FastAPI 中实现,使用 Starlette 的内部工具之一。

文档仍然可以正常工作,尽管不会添加任何说明该参数应包含路径的文档。

路径转换器

使用 Starlette 的直接选项,你可以通过类似于以下的 URL 声明包含 路径路径参数

/files/{file_path:path}

在此例中,参数名称为 file_path,最后的部分 :path 告诉它该参数应匹配任何 路径

因此,你可以这样使用它:

from fastapi import FastAPI

app = FastAPI()


@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
    return {"file_path": file_path}

提示

你可能需要参数包含 /home/johndoe/myfile.txt,并带有前导斜杠 (/)。

在这种情况下,URL 将是:/files//home/johndoe/myfile.txt,在 fileshome 之间带有双斜杠 (//)。

回顾

使用 FastAPI,通过简洁、直观且标准的 Python 类型声明,你可以获得:

  • 编辑器支持:错误检查、自动补全等。
  • 数据 "解析"
  • 数据验证
  • API 注解和自动文档

而且你只需要声明一次。

这可能是 FastAPI 相较于其他框架(除了原始性能之外)的主要可见优势。