如何使用Python的enum与FastAPI实现不区分大小写的选项?
实现FastAPI中Enum查询参数的大小写不敏感验证
问题场景
我有如下FastAPI应用代码:
import enum from typing import Annotated, Literal import uvicorn from fastapi import FastAPI, Query, Depends from pydantic import BaseModel app = FastAPI() class MyEnum(enum.Enum): ab = "ab" cd = "cd" class MyInput(BaseModel): q: Annotated[MyEnum, Query(...)] @app.get("/") def test(inp: MyInput = Depends()): return "Hello world" def main(): uvicorn.run("run:app", host="0.0.0.0", reload=True, port=8001) if __name__ == "__main__": main()
当使用curl http://127.0.0.1:8001/?q=ab或curl http://127.0.0.1:8001/?q=cd时,可正常返回"Hello World";但使用curl http://127.0.0.1:8001/?q=aB、curl http://127.0.0.1:8001/?q=AB等大小写混合或全大写参数时,会返回422 Unprocessable Entity错误。需要实现该验证的大小写不敏感。
解决方案
方法一:自定义Enum的匹配逻辑
通过重写Enum类的_missing_方法,让枚举在匹配输入值时忽略大小写:
import enum from typing import Annotated, Literal import uvicorn from fastapi import FastAPI, Query, Depends from pydantic import BaseModel app = FastAPI() class MyEnum(enum.Enum): ab = "ab" cd = "cd" @classmethod def _missing_(cls, value): # 遍历枚举成员,忽略大小写匹配 for member in cls: if member.value.lower() == value.lower(): return member return None class MyInput(BaseModel): q: Annotated[MyEnum, Query(...)] @app.get("/") def test(inp: MyInput = Depends()): return "Hello world" def main(): uvicorn.run("run:app", host="0.0.0.0", reload=True, port=8001) if __name__ == "__main__": main()
该方法利用Enum的_missing_钩子函数,当输入值无法直接匹配枚举成员时,自动执行大小写不敏感的匹配逻辑,返回对应的枚举成员。
方法二:使用Pydantic字段验证器
在Pydantic模型中添加字段验证器,将输入参数统一转为小写后再匹配枚举:
import enum from typing import Annotated, Literal import uvicorn from fastapi import FastAPI, Query, Depends from pydantic import BaseModel, field_validator app = FastAPI() class MyEnum(enum.Enum): ab = "ab" cd = "cd" class MyInput(BaseModel): q: Annotated[MyEnum, Query(...)] @field_validator('q', mode='before') def lowercase_q(cls, v): # 转换输入字符串为小写 if isinstance(v, str): return v.lower() return v @app.get("/") def test(inp: MyInput = Depends()): return "Hello world" def main(): uvicorn.run("run:app", host="0.0.0.0", reload=True, port=8001) if __name__ == "__main__": main()
通过field_validator的mode='before'参数,在字段解析为枚举类型之前,先将输入的字符串转为小写,确保能匹配到对应的枚举成员。
方法三:直接在Query中添加转换逻辑
如果不想修改Enum或Pydantic模型,可以直接在Query参数中指定转换函数:
import enum from typing import Annotated, Literal import uvicorn from fastapi import FastAPI, Query, Depends from pydantic import BaseModel app = FastAPI() class MyEnum(enum.Enum): ab = "ab" cd = "cd" class MyInput(BaseModel): q: Annotated[MyEnum, Query(..., transform=lambda v: v.lower() if isinstance(v, str) else v)] @app.get("/") def test(inp: MyInput = Depends()): return "Hello world" def main(): uvicorn.run("run:app", host="0.0.0.0", reload=True, port=8001) if __name__ == "__main__": main()
利用Query的transform参数,直接对输入的查询参数进行小写转换,逻辑简洁,不影响原有的Enum和模型定义。
内容的提问来源于stack exchange,提问作者Amin Ba
相关产品推荐
相关产品推荐

