fastapi+pydantic查询参数检查复杂参数



基于这个API定义,我的API支持如下查询:

GET http://my.api.url/posts?sort=["title","ASC"]&range=[0, 24]&filter={"q":"bar"}

,其中需要的一些检查是

  • sort[1]"asc""desc"(大小写不重要)
  • filter的键为"q"filter可以有其他密钥
  • range是一个包含两个整数的列表。range[0]小于或等于range[1]

在fastapi路径定义中,我目前将filter,sortrange定义为字符串,如下面的代码所示,使用json转换它们。加载,并执行检查。

@r.get(
"/users",
response_model=List[User],
response_model_exclude_none=True,
)
async def list_users(
filter: Optional[str] = None,
sort: Optional[str] = None,
range: Optional[str] = None,
...
):
...

我如何使用pydantic定义检查和API定义,而不是仅仅使用str,这样检查是pydantic完成的,而openapi模式定义更具描述性?

有一些方法可以做你想做的事情。但是FastAPI只允许序列结构(列表,元组,集合,序列)的查询和头参数,使&filter={"q":"bar"}它不会工作在FastAPI至少0.0.75版本,我正在工作。

如果你希望支持字典,你应该使用带有Body参数的POST方法。然后,您可以将您的值包装在Pydantic模型中以支持验证。

下面有两种方法可以在FastAPI中实现。

解决方案1:

class FilterModel(BaseModel):
filter: dict

@router.post(
"/posts"
)
async def list_users(
filters: FilterModel,
sort: Tuple[str, Literal["DESC", "ASC", "desc", "asc"]] = Query(("title", "ASC")),
ranges: Tuple[int, int] = Query((0, 24))
) -> None:
print(filters)
print(sort)
print(ranges)
model = MyModel(sort=sort, range=ranges)
print(model)

这将允许你调用这样的api:

POST http://my.api.url/posts?sort=title&sort=ASC&ranges=0&ranges=24

与身体:

{
"filter"={"q":"bar"}
}

解决方案2:

class MyModel(BaseModel):
sortBy: Optional[str]
sortOrder: Optional[Literal["DESC", "ASC", "desc", "asc"]]
min_range: Optional[int]
max_range: Optional[int]
class FilterModel(BaseModel):
filter: dict
@router.post(
"/posts"
)
async def list_users(
filters: FilterModel,
model: MyModel = Depends()
) -> None:
print(filters)
print(model)

这将允许你调用这样的api:

POST http://my.api.url/posts?sortBy="title"&sortOrder="ASC"&min_range=0&max_range=24

与身体:

{
"filter"={"q":"bar"}
}

如果你想使用GET方法和filter,你必须做一些hack的事情。