Joh*_* K. 5 validation pydantic fastapi
使用以下FastAPI后端:
from enum import Enum
from fastapi import FastAPI
class MyNumber(int, Enum):
ONE = 1
TWO = 2
THREE = 3
app = FastAPI()
@app.get("/add/{a}/{b}")
async def get_model(a: MyNumber, b: MyNumber):
return {"sum": a + b}
Run Code Online (Sandbox Code Playgroud)
当GET操作完成时:
curl -X 'GET' \
'http://127.0.0.1:8000/add/2/3' \
-H 'accept: application/json'
Run Code Online (Sandbox Code Playgroud)
返回以下内容:
{
"detail": [
{
"loc": [
"path",
"a"
],
"msg": "value is not a valid enumeration member; permitted: 1, 2, 3",
"type": "type_error.enum",
"ctx": {
"enum_values": [
1,
2,
3
]
}
},
{
"loc": [
"path",
"b"
],
"msg": "value is not a valid enumeration member; permitted: 1, 2, 3",
"type": "type_error.enum",
"ctx": {
"enum_values": [
1,
2,
3
]
}
}
]
}
Run Code Online (Sandbox Code Playgroud)
为什么会这样呢?甚至 Swagger UI 也将可能的值识别为整数:
IntEnum我已经尝试过使用替代(source )的解决方案,并且我可以确认它有效,但仍然 - 为什么它必须是这种方式?
enum.py源代码定义IntEnum为:
curl -X 'GET' \
'http://127.0.0.1:8000/add/2/3' \
-H 'accept: application/json'
Run Code Online (Sandbox Code Playgroud)
根据OP下面的评论做了一些挖掘。首先解释一下为什么这没有按预期工作:这就是 Starlette 处理路径参数的方式。当 Starlette 处理请求时(或者更准确地说,当Router调用APIRouter继承的对象时),它会将 确定path_params为一个字典,其中键是参数名称,值是其值。但是,当发生这种情况并且您没有在路径 string中指定类型时,它会自动将路径参数视为字符串。然后它将其添加到 中Request,基本上作为字典{"a":"1", "b":"2"}。然后,在调用堆栈的更上方,FastAPI 尝试查找与“1”对应的枚举值,这会抛出验证错误,因为"1" is not 1. 请注意,您的MyNumber类在这里还没有发挥任何作用。
此行为是设计使然,可能会受到如下影响:
@app.get("/add/{a:int}/{b:int}")
Run Code Online (Sandbox Code Playgroud)
这将确保 Starlette 将创建一个类似于 的 path_params 字典{"a":1, "b":2}。请注意,1和2现在是整数,并且将由 FastAPI 进行处理。
至于为什么MyNumber(IntEnum)开箱即用而MyNumber(int, Enum)不能,这是 Pydantic 的实现。我似乎无法准确指出具体情况,但是当ModelField为两个参数创建 时(在应用程序启动时发生utils.py -> create_response_field()),列表中validators包含int使用IntEnum.
但因为ModelField是使用创建的,functools.partial()所以我无法跟踪 Pydantic 的调用堆栈。所以,我不确定为什么会这样。
因此,简而言之,有两个选项可以解决此问题:要么强制 Starlette 解析路径参数a并bas int(使用),要么从您自己的枚举类{a:int}继承。IntEnum