我有以下代码:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Request(BaseModel):
user_name: str
age: int
# other unknown arguments
@app.post("/home")
def write_home(request: Request):
print(request.__dict__)
return {
"user_name": request.user_name,
"age": request.age,
# other arguments...
}
我希望请求采用可选参数(如
height
、weight
等),但这些参数可能是 unknown.
提前致谢
具有未知参数与 Pydantic(类型安全数据解析和验证)的意图完全相反。您可以做的(以及我会做的)是定义一个字段
extra
(或类似字段),用于保存动态额外数据:
从输入 import Any
class MyRequest(BaseModel):
user_name: str
age: int
extra: dict[str, Any]
然后你知道哪些字段总是需要存在,任何未知的都放在
extra
字段中。
我认为最简单的解决方案是使用extra = "allow"
设置
configure您的模型(默认设置为
extra = "ignore"
)。使用该设置,将任何额外的名称-值对传递给模型构造函数将使用提供的值和类型在该模型instance上动态创建字段。
举个例子:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Model(BaseModel):
user_name: str
age: int
class Config:
extra = "allow"
@app.post("/home")
def write_home(model: Model) -> Model:
print(model)
return model
现在您可以像这样发布任意附加数据,例如:
{
"user_name": "string",
"age": 0,
"height": 3.14
}
print
语句的输出为user_name='string' age=0 height=3.14
,响应正文与请求完全一致
我认为这里有一个大警告,它不是特定于 FastAPI,而是一般的 Pydantic 模型:
使用
extra = "allow"
设置,任何字段名称将可用。这可能会产生严重的意想不到的后果,因为提供的名称可以覆盖模型命名空间中的现有名称,包括内部属性(例如__fields__
)和预定义方法(例如dict
)的名称。
在 FastAPI 端点的上下文中,想象这样一种情况,某人
POST
像这样的有效负载:
{
"user_name": "string",
"age": 0,
"dict": 1
}
直到需要调用该实例的
dict
方法为止,这都可以正常工作,这恰好是响应形成过程中的情况。
换句话说,我们的
print(model)
似乎可以正常工作,产生 user_name='string' age=0 dict=1
,但是尝试从我们的路由处理程序 return 将 使用 TypeError: 'int' object is not callable
使服务器崩溃。
这只是一个例子,但这应该说明,如果你处理不当,为什么这可能是危险的或至少是有问题的。
您还需要注意的一些小注意事项:
您可以使用
await request.json()
(request
应该是 Starlette 的 Request
对象 的实例)将请求主体解析为 JSON,如here(参见选项 4)和 here(参见选项1).通过这种方式,您可以为 required和 known 参数获得
BaseModel
,同时仍然能够接受以前的 unknown 参数。
request.json()
将返回一个 dict
对象——如果您想知道如何循环遍历字典项,请参见here。
from fastapi import FastAPI, Request
from pydantic import BaseModel
app = FastAPI()
class Base(BaseModel):
username: str
age: int
@app.post('/')
async def main(base: Base, request: Request):
return await request.json()
输入示例(您可以在
http://127.0.0.1:8000/docs
使用 Swagger UI autodocs 来测试端点):
{
"username": "john",
"gender": "m",
"age": 20,
"height": 1.95,
"weight": 90
}
如果您根本不想使用 Pydantic
BaseModel
,您仍然可以使用 request.json()
将请求正文解析为 JSON,但是不会对您想要的必需/已知参数进行验证定义,除非您在端点内或在依赖类/函数中自行执行验证检查。如果您想这样做,请查看上面第一段中给出的链接答案,其中还演示了如何检查 JSON 对象的有效性并在客户端发送无效 JSON 时引发异常。在上面的示例中,此验证由 FastAPI 负责(由于使用了 Base
模型)。