路径参数¶
你可以使用与 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
,作为 Python 的 int
类型,而不是字符串 "3"
。
因此,通过这种类型声明,FastAPI 为你提供了自动请求解析。
数据验证¶
但如果你在浏览器中访问 http://127.0.0.1:8000/items/foo,你将看到一个友好的 HTTP 错误:
{
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"item_id"
],
"msg": "输入应为有效的整数,无法将字符串解析为整数",
"input": "foo",
"url": "https://errors.pydantic.dev/2.1/v/int_parsing"
}
]
}
因为路径参数 item_id
的值为 "foo"
,这不是一个 int
。
同样的错误也会出现在你提供一个 float
而不是 int
的情况下,例如:http://127.0.0.1:8000/items/4.2
Check
因此,通过相同的 Python 类型声明,FastAPI 为你提供了数据验证。
注意,错误信息也清楚地指出了验证未通过的具体位置。
这在开发和调试与你的 API 交互的代码时非常有帮助。
文档¶
当你在浏览器中打开 http://127.0.0.1:8000/docs 时,你将看到一个自动生成的、交互式的 API 文档,例如:
Check
同样,通过相同的 Python 类型声明,FastAPI 为你提供了自动的、交互式的文档(集成了 Swagger UI)。
注意,路径参数被声明为整数。
基于标准的优势,替代文档¶
由于生成的模式来自 OpenAPI 标准,有许多兼容的工具。
因此,FastAPI 本身提供了另一种 API 文档(使用 ReDoc),你可以在 http://127.0.0.1:8000/redoc 访问:
同样,有许多兼容的工具,包括许多语言的代码生成工具。
Pydantic¶
所有的数据验证都在幕后由 Pydantic 执行,因此你可以获得它带来的所有好处。你知道你处于良好的状态。
你可以将相同的类型声明与 str
、float
、bool
以及许多其他复杂数据类型一起使用。
这些内容将在教程的后续章节中探讨。
顺序很重要¶
在创建*路径操作*时,你可能会遇到固定路径的情况。
例如 /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
,“认为”它接收到的参数 user_id
的值为 "me"
。
同样,你不能重新定义路径操作:
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
并创建一个继承自 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 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"}
Tip
如果你好奇,"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
获取实际值(在这种情况下是 str
),或者通常使用 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 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"}
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 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}
Tip
你可能需要参数包含 /home/johndoe/myfile.txt
,带有前导斜杠(/
)。
在这种情况下,URL 将是:/files//home/johndoe/myfile.txt
,在 files
和 home
之间有两个斜杠(//
)。
总结¶
通过使用简短、直观且标准的 Python 类型声明,FastAPI 让你获得:
- 编辑器支持:错误检查、自动补全等。
- 数据 "解析"
- 数据验证
- API 注解和自动文档
你只需声明一次。
这可能是 FastAPI 相比其他框架(除了原始性能之外)的主要可见优势。