查询参数 Query Parameters
本教程共 50 篇 · 第 8 篇 · 更新于 2026-08-12 · 约 7 分钟阅读
本节目标:搞懂 URL 里
?后面那些键值对怎么变成函数参数,以及「有没有默认值」如何决定参数必填还是可选。
路径参数写在 URL 的「路径」里,比如 /items/5。还有一类参数写在 ? 后面,多个之间用 & 连接,比如 /items/?skip=0&limit=10。这部分就叫查询参数(query parameters)。
8-1 声明一个查询参数
在 FastAPI 里,只要函数参数不是路径参数、也不是后面要学的请求体,它就被自动当成查询参数。你不用额外标注,写个类型加默认值就行:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}
访问 http://127.0.0.1:8000/items/?skip=0&limit=10,返回:
{"skip": 0, "limit": 10}
查询参数本来就是 URL 里的字符串,但因为你写了 int,FastAPI 会自动把它们转成整数,再做校验。编辑器补全、文档生成这些好处和路径参数一模一样。
8-2 有默认值就是可选
查询参数不在固定路径里,所以它天生可以「不传」。上面 skip=0、limit=10 就是默认值。你不传它们也能正常访问:
- 访问
/items/→skip=0、limit=10(都用默认) - 访问
/items/?skip=20→skip=20、limit=10(只传一个) - 访问
/items/?skip=20&limit=5→ 两个都传
所以判断一个查询参数是不是「可选」,看的就是它有没有默认值。有默认值 = 可选。
8-3 用 None 声明可选参数
如果你想让参数可选、但又不给它一个具体默认值,就把默认值写成 None,类型用 str | None 表示「字符串或者没有」:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
if q:
return {"item_id": item_id, "q": q}
return {"item_id": item_id}
这里 item_id 是路径参数(在路径里),q 不是,所以 FastAPI 认出 q 是查询参数。访问 /items/5 时 q 是 None,返回 {"item_id": 5};访问 /items/5?q=hello 时返回 {"item_id": 5, "q": "hello"}。
NoteFastAPI 是靠「默认值 = None」判断参数可选的,不是靠
str | None这个类型注解。类型注解主要帮你获得编辑器提示和错误检查,默认值才是关键。
8-4 类型转换:bool 也很聪明
查询参数能自动转成 bool。而且 FastAPI 接受多种写法来表示真:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: str, short: bool = False):
if short:
return {"item_id": item_id, "short": True}
return {"item_id": item_id, "short": False}
下面这些 URL 里的 short 都会被当成 True:
/items/foo?short=1/items/foo?short=True/items/foo?short=true/items/foo?short=on/items/foo?short=yes
大小写、首字母大写也认。不传时使用默认值 False;但如果传了无法识别为布尔的值(比如 ?short=abc),FastAPI 会返回 422 校验错误,而不是悄悄当作 False。这种宽容转换在开关类参数(比如「是否精简返回」)上很实用。
8-5 没有默认值就是必填
查询参数不一定都可选。只要你不给默认值,它就是必填参数。访问时没传,FastAPI 会直接报错:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_user_item(item_id: str, needy: str):
return {"item_id": item_id, "needy": needy}
访问 /items/foo-item 会看到报错:
{
"detail": [
{
"type": "missing",
"loc": ["query", "needy"],
"msg": "Field required",
"input": null
}
]
}
loc 里是 ["query", "needy"],明确告诉你缺的是查询参数 needy。必须访问 /items/foo-item?needy=sooooneedy 才正常返回。
8-6 必填、默认值、可选混在一起
同一个函数里可以既有必填、又有带默认值、还有完全可选的查询参数,也能混着路径参数一起用:
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,
):
return {"item_id": item_id, "needy": needy, "skip": skip, "limit": limit}
这段代码的参数可以这样分类:
| 参数 | 声明方式 | 是否必填 | 说明 |
|---|---|---|---|
item_id | item_id: str | 必填 | 路径参数,在 URL 路径中 |
needy | needy: str | 必填 | 查询参数,无默认值 |
skip | skip: int = 0 | 可选 | 查询参数,默认 0 |
limit | limit: int | None = None | 可选 | 查询参数,默认 None |
8-7 多个路径与查询参数一起用
FastAPI 能同时认出多个路径参数和多个查询参数,而且顺序随便写,它靠「名字」匹配:
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 = {"user_id": user_id, "item_id": item_id}
if q:
item.update({"q": q})
if not short:
item.update({"description": "这是一段较长的描述文字"})
return item
访问 /users/5/items/pen?q=red&short=true,FastAPI 会正确把 5 给 user_id、pen 给 item_id、red 给 q、true 给 short。参数在函数里的先后顺序不影响识别。
Tip路径参数必需、查询参数可选是通用规律,但查询参数也能设成必填(不写默认值即可)。设计接口时,把「一定有」的放路径,「可有可无」或「可选过滤条件」放查询。
8-8 在文档页面试查询参数
不用写前端也能验证查询参数。启动后打开 http://127.0.0.1:8000/docs,点开接口旁边的「Try it out」,在 skip、limit、q 等框里填值,再点「Execute」,页面会直接拼出完整 URL 并展示返回的 JSON。这是调试查询参数最省事的办法,建议每次改完参数都去点一下确认效果。
如果你的参数带了 bool,文档里会显示下拉框,选 true 或 false 即可,不必手敲字符串。fastapi dev 自带的热重载也会让你改完代码立刻生效,调试体验很顺。
还有一个细节值得留意:查询参数在 URL 里都是文本,所以中文或空格会被浏览器编码成 %E4%B8%AD 这类形式。你不用手动解码,FastAPI 会先还原成原始字符串,再按你声明的类型转换,函数里拿到的就是正常的中文。同理,同名参数出现多次时默认只取其中一个,若需要接收一组值,得把类型声明成列表,这属于下一章要讲的进阶校验范围。
8-9 小结
这一章的关键就一句话:函数里不在路径里、也不是请求体的参数,都是查询参数。有默认值 = 可选,无默认值 = 必填,默认值是 None 表示「可选但允许为空」。FastAPI 会自动把字符串转成你声明的类型,bool 还认 1/true/on/yes 等多种写法。多个参数可以和路径参数混用,名字对上就行。
下一章我们给查询参数加更细的校验,比如字符串最长多少字、必须匹配某个正则。