第 3 章 · 路径参数与查询参数
本章目标:掌握路径参数的类型转换与校验、查询参数的可选/必选/列表声明,以及两者混用时 FastAPI 的识别规则与常见陷阱。
3.1 路径参数与自动类型转换
路径中用 {} 占位的部分就是路径参数,FastAPI 按函数签名的类型注解做解析和校验:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
def read_item(item_id: int):
# 注解为 int:请求 /items/5 时自动转换;
# 请求 /items/abc 时返回 422 校验错误,不会进入函数体
return {"item_id": item_id}请求 /items/abc 会得到结构化错误(HTTP 状态码 422):
{
"detail": [
{
"type": "int_parsing",
"loc": ["path", "item_id"],
"msg": "Input should be a valid integer, unable to parse string as an integer"
}
]
}这就是"类型即校验":你写的每个类型注解都同时是运行时防线和文档说明。
3.2 用 Enum 约束取值
想让路径参数只能取几个固定值,用枚举类:
from enum import Enum
from fastapi import FastAPI
app = FastAPI()
class ModelName(str, Enum):
# 继承 str 让成员可以直接参与字符串比较与序列化
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"
@app.get("/models/{model_name}")
def get_model(model_name: ModelName):
if model_name is ModelName.alexnet:
return {"model": model_name, "message": "Deep Learning FTW!"}
return {"model": model_name, "message": "Have some residuals"}访问 /models/resnet 正常;访问 /models/gpt4 返回 422,错误信息里还会列出合法值。文档页面上会渲染成下拉框。
3.3 声明顺序的坑(回顾与强化)
第 2 章讲过路由按序匹配,这里从参数角度再看一遍:
from fastapi import FastAPI
app = FastAPI()
# ✅ 固定路径在前
@app.get("/files/latest")
def latest_file():
return {"file": "report-2026.pdf"}
@app.get("/files/{file_path:path}")
def read_file(file_path: str):
# :path 转换器允许参数包含斜杠,可匹配多级路径
return {"file_path": file_path}:path 转换器是 Starlette 提供的能力,/files/a/b/c.txt 会得到 file_path="a/b/c.txt"——普通 str 参数遇到斜杠会 404。但即便如此,/files/latest 也必须写在前面,否则永远轮不到它。
3.4 查询参数
不在路径里、出现在 ?key=value&... 中的就是查询参数。函数签名中不是路径参数的那些标量参数会自动被当作查询参数:
from typing import Annotated # Python 3.9+ 可用 typing.Annotated
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/")
def read_items(
skip: int = 0, # 必有默认值 → 可选查询参数,缺省为 0
limit: int = 10,
q: str | None = None, # 可为 None 的可选字符串
):
# GET /items/?skip=20&limit=5&q=apple
return {"skip": skip, "limit": limit, "q": q}
@app.get("/search")
def strict_search(keyword: str):
# 无默认值 → 必选查询参数,缺失时返回 422
return {"keyword": keyword}判断规则只有三条:
- 参数名出现在路径模板里 → 路径参数;
- 参数是标量类型(int/str/bool/float 等)→ 查询参数;
- 参数是 Pydantic 模型 → 请求体(第 4 章)。
bool 类型特别贴心:?active=true、?active=1、?active=yes、?active=on 都会被正确解析为 True。
3.5 路径 + 查询参数混用
from fastapi import FastAPI
app = FastAPI()
@app.get("/users/{user_id}/orders")
def list_orders(
user_id: int, # 在路径模板中 → 路径参数
status: str = "all", # 标量且有默认值 → 查询参数
page: int = 1,
):
return {
"user_id": user_id,
"status": status,
"page": page,
# GET /users/42/orders?status=paid&page=3
}3.6 查询参数列表
一个 key 传多个值(如 /tags?a=1&a=2),用 list 类型接收:
from fastapi import FastAPI
app = FastAPI()
@app.get("/products/")
def filter_products(
tag: list[str] | None = None, # ?tag=food&tag=sale → ["food", "sale"]
size: list[int] = [10, 20], # 也可以有默认值列表
):
return {"tag": tag, "size": size}显式类型不能省
如果写 q: None = None 而不写 str | None,FastAPI 无法知道它的实际类型,会把参数当成 str 处理并可能在文档中标注错误。始终给查询参数写完整注解。
3.7 本章小结
- 路径参数按类型注解自动转换与校验,失败返回 422 与精确错误位置;
Enum(str, Enum)把参数约束成固定取值集合,文档自动生成下拉框;:path转换器支持含斜杠的多级路径参数,且仍受路由顺序规则约束;- 非路径的标量参数即查询参数:有默认值则可选,无默认值则必选;
- bool 查询参数兼容 true/false/1/0/on/off 等写法;
list[str]接收重复 key。
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. 请求 GET /items/abc,而函数签名是 def f(item_id: int),结果是?
2. 函数 def f(page: int = 1) 出现在 @app.get("/articles") 下,page 是什么?
3. 如何让路径参数支持包含斜杠的多级值,如 /files/docs/readme.md?
4. ?flag=on 发给参数 flag: bool,得到的值是?
🛠️ 动手实践
- 实现
GET /books/{category}:category 用 Enum 约束为 fiction/tech/history 三种,非法值观察 422 响应内容。 - 实现
GET /logs:接受可选的level(str)、since(int 时间戳)、lines(int 默认 100),并用 curl 组合测试各种省略情况。 - 用
list[str]实现一个多标签筛选接口,验证?tag=a&tag=b与?tag=单值两种请求的差异。
掌握了 URL 参数之后,进入第 4 章处理请求体。