跳到主要内容

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 数据。