如何让FastAPI正确解析作为查询参数的Pydantic模型Foo?
问题描述
我有一个FastAPI端点,接收Pydantic模型Foo作为查询参数,代码如下:
from typing import Annotated import uvicorn from fastapi import FastAPI, Query from pydantic import BaseModel app = FastAPI() class Foo(BaseModel): bar: str baz: dict[str, str] @app.get("/") def root(foo: Annotated[Foo, Query()]): return foo if __name__ == "__main__": uvicorn.run("test:app")
我通过Swagger定义查询参数,编码应该是正确的。我知道baz参数的语法看起来有些冗余(因为嵌套了字典),但即使不嵌套,解析也会失败。
当我调用该端点时:
curl -X 'GET' \ 'http://127.0.0.1:8000/?bar=sda&baz=%7B%22abc%22%3A%22def%22%7D' \ -H 'accept: application/json'
FastAPI无法正确读取Foo.baz,返回如下错误:
{ "detail": [ { "type": "dict_type", "loc": [ "query", "baz" ], "msg": "Input should be a valid dictionary", "input": "{\"abc\":\"def\"}" } ] }
我了解到可以通过dict(request.query_params)来读取字典,但这会绕过FastAPI的验证。我希望保持端点简洁并与代码库其他部分一致,继续使用Pydantic模型作为参数,请问如何让FastAPI正确解析Foo作为查询参数?
解决方案
FastAPI默认将查询参数视为字符串处理,因此传入的JSON字符串形式字典无法直接被解析为dict类型,可通过以下两种方式解决:
方法一:自定义Pydantic字段验证器
给baz字段添加前置验证器,将传入的JSON字符串转换为字典,同时保留类型校验:
from typing import Annotated import json import uvicorn from fastapi import FastAPI, Query from pydantic import BaseModel, field_validator app = FastAPI() class Foo(BaseModel): bar: str baz: dict[str, str] @field_validator('baz', mode='before') def parse_baz(cls, v): if isinstance(v, str): try: return json.loads(v) except json.JSONDecodeError: raise ValueError("baz必须是合法的JSON字符串") return v @app.get("/") def root(foo: Annotated[Foo, Query()]): return foo if __name__ == "__main__": uvicorn.run("test:app")
方法二:使用Pydantic的Json类型标注
直接用Annotated结合Json类型,明确告知FastAPI需要解析JSON字符串:
from typing import Annotated import uvicorn from fastapi import FastAPI, Query from pydantic import BaseModel, Json app = FastAPI() class Foo(BaseModel): bar: str baz: Annotated[dict[str, str], Json()] @app.get("/") def root(foo: Annotated[Foo, Query()]): return foo if __name__ == "__main__": uvicorn.run("test:app")
可选的请求格式优化
除了JSON编码的方式,还可以使用HTTP查询参数的数组式语法传递字典,无需编码更直观:
curl -X 'GET' \ 'http://127.0.0.1:8000/?bar=sda&baz[abc]=def' \ -H 'accept: application/json'
FastAPI会自动将baz[abc]=def解析为{"abc": "def"},两种方法都兼容该请求格式。
内容的提问来源于stack exchange,提问作者Matt
相关产品推荐
相关产品推荐

