FastAPI 查询参数(Query Parameters)完全指南:默认值、可选与必填、布尔转换一网打尽
本篇指南基于仓库内
docs/de/docs/tutorial/query-params.md(对应英文版 query-params.md)及配套可运行示例编写,系统讲解 FastAPI 中查询参数(Query Parameters)的声明方式、URL 解析规则、默认值/可选性/必填性三态模型以及bool类型转换行为。读完你既能用普通函数参数写出分页、过滤等典型查询参数接口,也能从源码与测试层面理解 FastAPI 是如何自动区分路径参数与查询参数的。
一、什么是查询参数:URL 中 ? 之后的键值对
HTTP 请求的 URL 中可以携带一组查询参数(Query,也叫 query string)。它由 ? 之后、以 & 分隔的多个"键=值"对组成。例如在 URL:
http://127.0.0.1:8000/items/?skip=0&limit=10
中就包含了两个查询参数:
skip:值为0limit:值为10
查询参数是 URL 的一部分,因此它们在"本性"上是字符串——无论你写入 0 还是 10,到达服务端时最初都是字符串 "0"、"10"。
但 FastAPI 的强大之处在于:当你在路径操作函数中把这些参数用 Python 类型标注声明(例如标注为 int)时,FastAPI 会自动把它们转换(parse/convert)成对应类型,并按该类型校验。它与路径参数享受同一套完整流程:
- 编辑器支持(IDE 补全、类型提示)
- 数据"解析"——把来自 HTTP 请求的字符串转换为 Python 数据
- 数据校验
- 自动生成交互式 API 文档
二、极速上手:把非路径参数变成查询参数
FastAPI 的约定非常简洁:凡是函数里声明了、又不是路径参数的其他参数,一律被自动当作查询参数。
配套示例 tutorial001_py310.py 演示了最经典的分页场景:
from fastapi import FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
@app.get("/items/")
async def read_item(skip: int = 0, limit: int = 10):
return fake_items_db[skip : skip + limit]
这段代码声明了两个查询参数:
skip: int = 0:切片起点,默认0;limit: int = 10:切片条数,默认10。
通过 fake_items_db[skip : skip + limit] 实现数据库"假分页"。将本文件保存为 main.py 后,在仓库环境执行 fastapi dev main.py(或 uvicorn main:app --reload)即可启动:
- 访问
http://127.0.0.1:8000/items/?skip=0&limit=10,函数收到的就是skip=0、limit=10,返回三条数据; - 交互式文档位于
http://127.0.0.1:8000/docs,此时你能在 Swagger UI 上看到自动生成的skip、limit两个查询参数输入框——这就是"自动文档"特性的直观体现。
三、默认值:查询参数天然"可选"
由于查询参数不是路径的固定组成部分,它们天然可以是可选的,并且可以有默认值。
接续上面的例子:skip 默认 0、limit 默认 10。于是访问不带任何查询字符串的:
http://127.0.0.1:8000/items/
等价于访问:
http://127.0.0.1:8000/items/?skip=0&limit=10
而如果你显式提供部分参数,例如:
http://127.0.0.1:8000/items/?skip=20
那么函数收到的实际参数值为:
skip = 20:因为你已在 URL 中显式设置;limit = 10:因为未提供,回落至默认值。
注意这里"默认值 = 未在 URL 中出现"的回退语义,是后续理解必填/可选参数的关键前提。
四、可选参数:把默认值设为 None
想让某个查询参数完全可选(可以不出现在 URL 中)时,只需把它的默认值设为 None。示例 tutorial002_py310.py:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: str, q: str | None = None):
if q:
return {"item_id": item_id, "q": q}
return {"item_id": item_id}
这里函数参数 q 被声明为 str | None = None,因此它成为可选查询参数,请求未携带 q 时其值就是 None,函数只返回 {"item_id": item_id};携带 q 时则额外返回其值。
细心的读者会发现:同一个函数里同时出现了 `item_id`(出现在路径 `/items/{item_id}` 中)和 `q`。**FastAPI 足够智能**:`item_id` 因出现在路径模板中而被识别为路径参数,而 `q` 不在路径里,自然就被归类为查询参数——无需你做任何额外标记。
这种"按名称与位置自动区分"的能力,正是本指南第五节到第八节所有写法的基石。
五、类型转换:bool 查询参数怎么"真"
查询参数不仅能转成 int,同样能声明为 bool 并自动完成字符串→布尔值转换。示例 tutorial003_py310.py:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: str, q: str | None = None, short: bool = False):
item = {"item_id": item_id}
if q:
item.update({"q": q})
if not short:
item.update(
{"description": "This is an amazing item that has a long description"}
)
return item
访问下面的任意 URL,函数收到的 short 参数值都会是 True:
http://127.0.0.1:8000/items/foo?short=1
http://127.0.0.1:8000/items/foo?short=True
http://127.0.0.1:8000/items/foo?short=true
http://127.0.0.1:8000/items/foo?short=on
http://127.0.0.1:8000/items/foo?short=yes
以及其他任何大小写变体(全大写、首字母大写……如 ON、Yes、TRUE),都会把 short 解析为布尔值 True;否则为 False。换句话说,bool 转换是对大小写不敏感的。
在实际接口中,这种写法非常适用于"是否返回完整描述""是否包含明细"等开关型参数——上例中 short=False(默认)时返回详细 description,short=True 时只返回精简字段。上述五个 URL 与对应行为的断言,可以在仓库测试 test_query_params/test_tutorial003.py 中逐条找到。
六、混合声明:多个路径参数 + 多个查询参数
你完全可以在同一个函数里同时声明多个路径参数和多个查询参数,FastAPI 总能分清谁是谁——关键在于按参数名识别,而不是按声明顺序。示例 tutorial004_py310.py:
from fastapi import FastAPI
app = FastAPI()
@app.get("/users/{user_id}/items/{item_id}")
async def read_user_item(
user_id: int, item_id: str, q: str | None = None, short: bool = False
):
item = {"item_id": item_id, "owner_id": user_id}
if q:
item.update({"q": q})
if not short:
item.update(
{"description": "This is an amazing item that has a long description"}
)
return item
这个接口包含:
| 参数 | 位置类型 | 类型 | 默认值 |
|---|---|---|---|
user_id |
路径参数 | int |
无(必填) |
item_id |
路径参数 | str |
无(必填) |
q |
查询参数 | str | None |
None(可选) |
short |
查询参数 | bool |
False |
即便把参数顺序任意打乱(例如把 q 写在 user_id 前面),FastAPI 依然能通过路径模板与类型标注精确判定各自归属。这也印证了该教程的设计哲学:"路径模板决定路径参数,其余全部归属查询"。运行测试见 test_query_params/test_tutorial004.py。
七、必填查询参数:不给默认值即可
前文规则可总结为:对非路径参数声明了默认值,它就不是必填的;而如果只是想让它"可选且默认无值",把默认值设成 None。
那么想让查询参数必填怎么办?答案很简单——不声明任何默认值。示例 tutorial005_py310.py:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_user_item(item_id: str, needy: str):
item = {"item_id": item_id, "needy": needy}
return item
此处查询参数 needy 是一个必填的 str。若在浏览器中直接访问:
http://127.0.0.1:8000/items/foo-item
即 URL 中没有携带必需的 needy,FastAPI 会返回类似如下的校验错误 JSON:
{
"detail": [
{
"type": "missing",
"loc": [
"query",
"needy"
],
"msg": "Field required",
"input": null
}
]
}
可以看到 loc 字段明确指出了出错位置:"query" 作用域下名为 "needy" 的参数缺失,type 为 "missing"。这是 FastAPI 内置校验器(底层由 Pydantic 驱动)产出的标准结构,HTTP 响应码为默认的校验失败状态(细节可参考 handling-errors.md)。
补上该参数即可正常访问:
http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
返回:
{
"item_id": "foo-item",
"needy": "sooooneedy"
}
八、三态齐备:必填 + 带默认值 + 完全可选
真实业务中常常需要同一接口内混合三种参数形态。示例 tutorial006_py310.py 给出了最终形态:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_user_item(
item_id: str, needy: str, skip: int = 0, limit: int | None = None
):
item = {"item_id": item_id, "needy": needy, "skip": skip, "limit": limit}
return item
这个接口里共存着 3 个查询参数:
needy:必填的str,没有默认值,缺失即报missing错误;skip:int,带默认值0,可省略;limit:可选的int,声明为int | None = None,可省略且省略时为None。
注意 needy 与 skip、limit 的区别:skip 省略时会有确定值 0,而 limit 省略时是 None(表示"调用方未指定上限"),这为业务逻辑区分"未传"与"传了 0"提供了依据。对应测试见 test_query_params/test_tutorial006.py。
进阶用法:查询参数同样可以使用 `Enum` 枚举类型,限定其只能取若干预定义值——用法与[路径参数中的预定义值](https://gitcode.com/GitHub_Trending/fa/fastapi/blob/50113da16fec53b66b80d75e80a89296de4fa5a5/docs/de/docs/tutorial/path-params.md?utm_source=gitcode_repo_files#predefined-values)完全一致。而更丰富的声明能力(`Query` 显式声明、长度/数值校验等)见后续教程 [query-params-str-validations.md](https://gitcode.com/GitHub_Trending/fa/fastapi/blob/50113da16fec53b66b80d75e80a89296de4fa5a5/docs/en/docs/tutorial/query-params-str-validations.md?utm_source=gitcode_repo_files)。
九、源码佐证:FastAPI 究竟如何识别查询参数
上述"魔法"在框架源码中有着清晰的落点。在 fastapi/params.py 中可以看到查询参数描述符的定义:
class Query(Param): # type: ignore[misc]
in_ = ParamTypes.query
def __init__(
self,
default: Any = Undefined,
*,
default_factory: Callable[[], Any] | None = _Unset,
...
):
ParamTypes.query标记了该类参数所属的请求位置(query string),这正是 FastAPI 内部把"非路径参数"归属到查询参数位置的组织方式;default: Any = Undefined与默认值机制直接相关:你在函数签名里写的= 10、= None会进入该默认值解析路径,而没有默认值的needy会保持"必填"语义;- 声明了默认值的简单写法(如
skip: int = 0)会被 FastAPI 在路由注册时自动包装成Query(...)描述符,因此教程正文没有出现任何额外的Queryimport——这是"尽量简单"设计的体现。
配合类型注解,FastAPI 在路由构建阶段便能确定:
- 参数是否出现在路径模板中(是 → 路径参数,且不允许有默认值);
- 否则即为查询参数,并根据 Python 类型(
int/bool/str/None)确定解析与校验规则; - 生成的 OpenAPI 中为每个查询参数生成 schema 条目,进而在
/docs自动文档中渲染出输入框与必填标记。
仓库 tests/test_tutorial/test_query_params/ 下与 tutorial001~tutorial006 一一对应的测试文件,覆盖了默认值、可选、必填、布尔转换等全部行为,可作为行为规格直接查阅。
十、小结与下一步
小结本节学到的查询参数核心规则:
| 声明方式 | 参数性质 | 缺省行为 |
|---|---|---|
skip: int = 0 |
有默认值的查询参数 | 取默认值 |
q: str | None = None |
可选查询参数 | 为 None |
needy: str(无默认值) |
必填查询参数 | 返回校验错误 |
short: bool = False |
布尔查询参数 | 1/true/on/yes/True(任意大小写)→ True |
掌握这些之后,你已能独立写出带分页、可选过滤与布尔开关的真实接口。更进一步的约束能力——例如给查询参数加最小/最大长度、正则校验、显式标题与描述等——请继续阅读 查询参数与字符串校验,并在交互式文档中实时观察每一项约束对 OpenAPI 描述的影响。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00