FastAPI官档精编006 - 路径参数

Python大咖谈

共 10032字,需浏览 21分钟

 ·

2021-09-29 11:05

呆鸟云:发布本系列旨在推广 FastAPI 以及推进 FastAPI 中文官档翻译,目前,已完成 98% 的中文官档翻译,如果您对 FastAPI 有兴趣,可以为这个很赞的开源项目做些贡献,比如校译、翻译、审阅等。

开源项目的发展离不开大家的支持。当然最简单的就是在 Github 上点 Star 了。

如果觉得 FastAPI 不错,您也可以转发、转载本文,让更多人知道 Python 还有这么简单、快速的后端支持库。

FastAPI 官档地址:

https://fastapi.tiangolo.com/zh/

下面先列出几个需要 Review 的 PR,希望大家多多参与。

  • https://github.com/tiangolo/fastapi/pull/3826

  • https://github.com/tiangolo/fastapi/pull/3827

  • https://github.com/tiangolo/fastapi/pull/3828

  • https://github.com/tiangolo/fastapi/pull/3829

  • https://github.com/tiangolo/fastapi/pull/3830

  • https://github.com/tiangolo/fastapi/pull/3832

  • https://github.com/tiangolo/fastapi/pull/3793

  • https://github.com/tiangolo/fastapi/pull/3795

  • https://github.com/tiangolo/fastapi/pull/3796

以下为正文。


FastAPI 使用 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

!!! check "检查"

类型声明将为函数提供错误检查、代码补全等编辑器支持。

数据转换

运行示例并访问 http://127.0.0.1:8000/items/3,返回的响应如下:

{"item_id":3}

!!! check "检查"

注意,函数接收并返回的值是 `3`( `int`),不是 `"3"``str`)。

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

这是因为路径参数 item_id 的值 ("foo")的类型不是 int

值的类型不是 int 而是浮点数(float)时也会显示同样的错误,比如:http://127.0.0.1:8000/items/4.2

!!! check "检查"

**FastAPI** 使用 Python 类型声明实现了数据校验。

注意,上面的错误指明了未通过校验的具体原因。

这在开发调试与 API 交互的代码时非常有用。

查看文档

访问 http://127.0.0.1:8000/docs,查看自动生成的 API 文档:

!!! check "检查"

还是使用 Python 类型声明,**FastAPI** 提供了(集成 Swagger UI 的)API 文档。

注意,路径参数的类型是整数。

基于标准的好处,备选文档

FastAPI 使用 OpenAPI 生成概图,所以能兼容很多工具。

FastAPI 还内置了 ReDoc 生成的备选 API 文档,可在此查看 http://127.0.0.1:8000/redoc

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

Pydantic

FastAPI 充分地利用了 Pydantic 的优势,用它在后台校验数据。众所周知,Pydantic 擅长的就是数据校验。

同样,strfloatbool 以及很多复合数据类型都可以使用类型声明。

下一章介绍详细内容。

顺序很重要

有时,路径操作中的路径是写死的。

比如要使用 /users/me 获取当前用户的数据。

然后还要使用 /users/{user_id},通过用户 ID 获取指定用户的数据。

由于路径操作是按顺序依次运行的,因此,一定要在 /users/{user_id} 之前声明 /users/me

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,FastAPI 会认为正在接收值为 "me"user_id 参数。

预设值

路径操作使用 Python 的 Enum 类型接收预设的路径参数

创建 `Enum` 类

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

通过从 str 继承,API 文档就能把值的类型定义为字符串,并且能正确渲染。

然后,创建包含固定值的类属性,这些固定值是可用的有效值:

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

!!! info "说明"

Python 3.4 及之后版本支持枚举(即 enums)。

!!! tip "提示"

**AlexNet**、**ResNet**、**LeNet** 是机器学习<abbr title="技术上来说是深度学习模型架构">模型</abbr>

声明路径参数

使用 Enum 类(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 == 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"}

查看文档

API 文档会显示预定义路径参数的可用值:

使用 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 == 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.valueyour_enum_member.value 获取实际的值(本例中为字符串):

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

!!! tip "提示"

使用 `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 == 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 不支持声明包含路径的路径参数,因为这会导致测试和定义更加困难。

不过,仍可使用 Starlette 内置工具在 FastAPI 中实现这一功能。

而且不影响文档正常运行,但是不会添加该参数包含路径的说明。

路径转换器

直接使用 Starlette 的选项声明包含路径路径参数

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

!!! tip "提示"

注意,包含 `/home/johndoe/myfile.txt` 的路径参数要以斜杠(`/`)开头。

本例中的 URL 是 `/files//home/johndoe/myfile.txt`。注意,`files` 和 `home` 之间要使用**双斜杠**`//`)。

小结

通过简短、直观的 Python 标准类型声明,FastAPI 可以支持:

  • 编辑器支持:错误检查,代码自动补全等

  • 数据解析

  • 数据校验

  • API 注解和 API 文档

只需要声明一次即可。

这可能是除了性能以外,FastAPI 与其它框架相比的主要优势。

如果您对 FastAPI 感兴趣,请在公号后台输入 fastapi

浏览 16
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报