2.2 查询参数
本章目标:掌握 URL 问号后面的参数(查询参数)的声明、默认值、可选参数和校验。
1. 什么是查询参数
GET /items?page=2&size=10&keyword=手机
URL 中 ? 后面的 key=value 对(用 & 分隔)就是查询参数,常用于过滤、分页、搜索等场景。
在 FastAPI 中,函数参数只要没出现在路径里,就自动被当作查询参数:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items")
def list_items(page: int = 1, size: int = 10):
return {"page": page, "size": size}
- 访问
/items?page=2&size=20→{"page": 2, "size": 20} - 访问
/items(不传参数)→ 使用默认值{"page": 1, "size": 10} - 访问
/items?page=abc→ 422 自动校验错误
2. 必填 vs 可选
没有默认值 = 必填:
@app.get("/search")
def search(keyword: str): # 没有默认值,必须传
return {"keyword": keyword}
不传 keyword 访问 /search 会得到 422:Field required。
给默认值 = 可选:
@app.get("/search")
def search(keyword: str = ""):
return {"keyword": keyword}
允许"不传"这个语义时,用 None 作默认值:
@app.get("/search")
def search(keyword: str | None = None):
if keyword is None:
return {"message": "未指定关键词,返回全部"}
return {"message": f"搜索:{keyword}"}
str | None表示「字符串或 None」(Python 3.10+ 语法)。老代码里的Optional[str]是同样的意思。
3. 布尔类型的自动转换
@app.get("/items")
def list_items(published: bool = False):
return {"published": published}
以下写法都会被识别为 True:?published=true、?published=1、?published=yes、?published=on。这在做「开关」类过滤时很方便。
4. 路径参数 + 查询参数混用
FastAPI 会自动区分:在路径模板里的就是路径参数,其余是查询参数。
@app.get("/users/{user_id}/posts")
def list_user_posts(
user_id: int, # 路径参数
page: int = 1, # 查询参数
only_published: bool = True, # 查询参数
):
return {"user_id": user_id, "page": page, "only_published": only_published}
访问:/users/1/posts?page=2&only_published=false
5. 用 Query 做进一步校验
和 Path 类似,查询参数用 Query 加约束:
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items")
def list_items(
page: Annotated[int, Query(ge=1, description="页码")] = 1,
size: Annotated[int, Query(ge=1, le=100, description="每页数量,最大100")] = 10,
keyword: Annotated[str | None, Query(min_length=1, max_length=50)] = None,
):
return {"page": page, "size": size, "keyword": keyword}
常用校验项:
| 参数 | 作用 | 适用 |
|---|---|---|
| ge / gt / le / lt | 数值范围 | int, float |
| min_length / max_length | 字符串长度 | str |
| pattern | 正则匹配 | str |
| description | 文档说明 | 所有 |
例如限制 size 最大 100 可以防止有人传 ?size=999999 把数据库拖垮——列表接口永远要限制每页最大数量,这是实际开发经验。
6. 接收多个同名参数(列表)
GET /items?tag=python&tag=fastapi
想接收多个 tag,声明为列表类型(必须配合 Query):
@app.get("/items")
def list_items(tag: Annotated[list[str] | None, Query()] = None):
return {"tags": tag}
访问 /items?tag=python&tag=fastapi → {"tags": ["python", "fastapi"]}
7. 参数别名
URL 里想用 item-type(Python 变量名不能有 -):
@app.get("/items")
def list_items(
item_type: Annotated[str | None, Query(alias="item-type")] = None
):
return {"item_type": item_type}
访问 /items?item-type=book 即可。
8. 实战:一个典型的列表接口签名
综合本章内容,实际项目中的列表接口一般长这样(先返回假数据,第四部分接上数据库):
@app.get("/articles")
def list_articles(
page: Annotated[int, Query(ge=1)] = 1,
size: Annotated[int, Query(ge=1, le=100)] = 10,
keyword: Annotated[str | None, Query(max_length=50)] = None,
category: str | None = None,
only_published: bool = True,
):
return {
"page": page,
"size": size,
"filters": {
"keyword": keyword,
"category": category,
"only_published": only_published,
},
"items": [], # 以后从数据库查
}
打开 /docs 看看这个接口的参数表单,每个参数的类型、默认值、约束一目了然。
本章小结
- 不在路径里的函数参数就是查询参数
- 有默认值可选,无默认值必填;
xxx: 类型 | None = None表示真正可选 Query()提供长度、范围、正则等校验- 列表接口一定要限制分页大小上限
下一章:2.3 请求体与 Pydantic 模型 —— 学习接收 JSON 数据。