呆鸟云:发布本系列旨在推广 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 擅长的就是数据校验。
同样,str
、float
、bool
以及很多复合数据类型都可以使用类型声明。
下一章介绍详细内容。
顺序很重要
有时,路径操作中的路径是写死的。
比如要使用 /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
并创建继承自 str
和 Enum
的子类。
通过从 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** 是机器学习模型。
声明路径参数
使用 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.value
或 your_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 与其它框架相比的主要优势。