fastapi 如何让 Query 参数支持单值与 List 同时兼容

11次阅读

fastapi中Query参数需手动归一化才能同时接受字符串和列表:用union[str, List[str]]注解配合Depends封装解析逻辑,对str调用split(“,”)并strip,对List[str]直接处理,返回List[str]以确保依赖注入生效。

fastapi 如何让 Query 参数支持单值与 List 同时兼容

FastAPI 中 Query 参数如何同时接受字符串和列表

默认情况下,FastAPI 的 Query 会根据类型注解做严格解析:声明为 List[str] 就只认逗号分隔或重复参数;声明为 str 就只收单个值。要两者都兼容,不能靠类型注解自动推导,得手动干预解析逻辑。

Union[str, List[str]] + 自定义解析函数

FastAPI 支持 Union 类型,但仅靠它还不够——它会让 OpenAPI 文档混乱,且对单个字符串输入(如 ?tags=python)仍报错,因为 FastAPI 默认把单个值当 str,而你期望它也能进 List 流程。

实操建议:

  • 类型注解写成 Union[str, List[str]],并设默认值为 None
  • 路由函数内部,用 isinstance(value, str) 判断是否需要包装成列表:[value]
  • 如果前端传的是 ?tags=a&tags=b,FastAPI 已自动转成 ["a", "b"];如果是 ?tags=a,b,需额外用 .split(",") 处理(但注意空格、编码问题)
  • 更稳妥的做法是统一用重复参数形式(?tag=a&tag=b),避免逗号解析歧义

Depends 封装兼容逻辑

把判断和归一化逻辑抽成依赖,既复用又保持路由干净。适合多处需要同样行为的场景。

示例:

from fastapi import Depends, Query from typing import Union, List, Optional  def parse_tags(tag: Union[str, List[str]] = Query(None)) -> List[str]:     if tag is None:         return []     if isinstance(tag, str):         return [t.strip() for t in tag.split(",") if t.strip()]     return [t.strip() for t in tag if t.strip()]  @app.get("/items/") def list_items(tags: List[str] = Depends(parse_tags)):     return {"tags": tags}

注意点:

  • 这个函数返回类型必须是 List[str],否则 FastAPI 不会把它当依赖注入
  • 不要在依赖里抛异常(如 HTTPException),FastAPI 会吞掉错误信息;改用 raise RequestValidationError 或让 FastAPI 自动校验
  • OpenAPI 文档中该参数仍显示为 String,因为依赖函数没有类型提示穿透能力

为什么不用 ... = Query(default=[]) 直接设默认空列表

看似简单,但会出问题:当用户完全不传该参数时,确实得到空列表;可一旦传了单个字符串(?tag=go),FastAPI 会尝试把字符串“go”赋给 List[str] 类型变量,直接报 validation error,而不是自动转成 ["go"]

根本原因:FastAPI 的类型转换器(sequence 转换器)只在明确声明为 List[...] 且请求含多个同名参数时触发,不处理单字符串到单元素列表的隐式转换

所以绕不过手动归一化——要么在路径函数里写 if isinstance(...),要么用 Depends 封装。没更短的路。

text=ZqhQzanResources