Skip to content

第 3 章 · 路径参数与查询参数

本章目标:掌握路径参数的类型转换与校验、查询参数的可选/必选/列表声明,以及两者混用时 FastAPI 的识别规则与常见陷阱。

3.1 路径参数与自动类型转换

路径中用 {} 占位的部分就是路径参数,FastAPI 按函数签名的类型注解做解析和校验:

python
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):

json
{
  "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 约束取值

想让路径参数只能取几个固定值,用枚举类:

python
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 章讲过路由按序匹配,这里从参数角度再看一遍:

python
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&... 中的就是查询参数。函数签名中不是路径参数的那些标量参数会自动被当作查询参数:

python
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}

判断规则只有三条:

  1. 参数名出现在路径模板里 → 路径参数
  2. 参数是标量类型(int/str/bool/float 等)→ 查询参数;
  3. 参数是 Pydantic 模型 → 请求体(第 4 章)。

bool 类型特别贴心:?active=true?active=1?active=yes?active=on 都会被正确解析为 True

3.5 路径 + 查询参数混用

python
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 类型接收:

python
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,得到的值是?

🛠️ 动手实践

  1. 实现 GET /books/{category}:category 用 Enum 约束为 fiction/tech/history 三种,非法值观察 422 响应内容。
  2. 实现 GET /logs:接受可选的 level(str)、since(int 时间戳)、lines(int 默认 100),并用 curl 组合测试各种省略情况。
  3. list[str] 实现一个多标签筛选接口,验证 ?tag=a&tag=b?tag= 单值两种请求的差异。

掌握了 URL 参数之后,进入第 4 章处理请求体。