FastAPI: 路径参数与查询参数 — 精准路由设计
最后更新:2026-08-26
路由设计就像城市道路规划——路径参数是门牌号(精准定位),查询参数是筛选条件(缩小范围),两者配合才能快速找到目标。
1. 你将学到
- 路径参数:类型自动转换、校验与
Path()约束(gt/ge/lt/le) - 查询参数:可选/必选、默认值、
Query()高级校验(alias/description/deprecated) - 多参数组合顺序规则与常见陷阱
- 字符串枚举参数:
Enum在路径与查询中的应用 - Alice 的 PriceTracker 场景:按商品 ID 查价格、按品类+价格区间筛选
2. Alice 的真实故事
(1) 痛点:商品查询 API 参数混乱
Alice 的 PriceTracker 需要支持多种查询方式:按商品 ID 精确查询、按品类和价格区间筛选、按排序方式分页浏览。Bob 前端传了 category=electronics&min_price=10&max_price=999,但 Alice 的 Flask 代码里手动解析每个参数,类型转换容易出错,负数价格和非法排序字段没有校验,线上事故频发。
(2) FastAPI 参数校验的解法
FastAPI 用类型提示自动解析和校验参数,Path() 和 Query() 提供声明式约束,参数不合法自动返回 422 错误。
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/products/{product_id}")
async def get_product(
product_id: int = Path(gt=0, description="Product ID must be positive"),
category: str | None = Query(None, max_length=50),
):
return {"product_id": product_id, "category": category}
(3) 收益
参数校验代码从 30 行降到 3 行,422 错误响应自动包含具体校验失败信息,Bob 一看就知道哪个参数不对,API 文档也自动展示所有约束条件。
3. 路径参数详解
(1) 基础路径参数
路径参数是 URL 路径的一部分,用 {param} 语法定义,FastAPI 自动按类型提示转换。
sequenceDiagram
participant Client
participant Router as FastAPI Router
participant Converter as Type Converter
participant Validator as Path Validator
participant Handler as View Function
Client->>Router: GET /products/42
Router->>Converter: Extract "42" from path
Converter->>Converter: int("42") → 42
Converter->>Validator: product_id=42 (int)
Validator->>Validator: Check gt=0 → 42 > 0 ✓
Validator->>Handler: get_product(product_id=42)
Handler-->>Client: {"product_id": 42}
| 路径参数类型 | URL 示例 | Python 类型 | 自动转换 |
|---|---|---|---|
| 整数 | /products/42 |
int |
"42" → 42 |
| 浮点数 | /prices/9.99 |
float |
"9.99" → 9.99 |
| 字符串 | /categories/electronics |
str |
原样 |
| 路径 | /files/src/main.py |
Path |
含 / 的字符串 |
▶ 示例:基础路径参数与类型转换
from fastapi import FastAPI
app = FastAPI()
@app.get("/products/{product_id}")
async def get_product(product_id: int):
# FastAPI auto-converts "42" to int(42)
# If user sends /products/abc → 422 error
return {"product_id": product_id, "type": str(type(product_id))}
输出:
INFO: 127.0.0.1:50123 - "GET /api/items HTTP/1.1" 200 OK
输出(
/products/42):
{"product_id": 42, "type": "<class 'int'>"}
(2) Path() 约束校验
Path() 为路径参数添加数值范围、字符串长度等约束,自动体现在 OpenAPI 文档中。
| 约束参数 | 适用类型 | 含义 |
|---|---|---|
gt |
int/float | 大于 (>) |
ge |
int/float | 大于等于 (>=) |
lt |
int/float | 小于 (<) |
le |
int/float | 小于等于 (<=) |
min_length |
str | 最小长度 |
max_length |
str | 最大长度 |
pattern |
str | 正则匹配 |
description |
所有 | OpenAPI 描述 |
▶ 示例:Path() 数值约束
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/products/{product_id}")
async def get_product(
product_id: int = Path(
gt=0,
le=1000000,
description="Product ID: positive integer, max 1 million",
),
):
return {"product_id": product_id}
输出:
INFO: 127.0.0.1:50123 - "GET /api/items HTTP/1.1" 200 OK
输出(
/products/-1返回 422):
{
"detail": [
{
"loc": ["path", "product_id"],
"msg": "Input should be greater than 0",
"type": "greater_than"
}
]
}
4. 查询参数详解
(1) 基础查询参数
查询参数是 URL 中 ? 后面的键值对,用函数参数声明,有默认值的参数是可选的。
| 查询参数类型 | 声明方式 | 是否必选 |
|---|---|---|
| 必选 | category: str |
是 |
| 可选(默认值) | category: str = "all" |
否 |
| 可选(None) | `category: str | None = None` |
▶ 示例:查询参数基础
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/prices")
async def search_prices(
category: str | None = Query(None, max_length=50, description="Product category"),
min_price: float = Query(0.0, ge=0, description="Minimum price in USD"),
max_price: float = Query(999999.0, le=999999, description="Maximum price in USD"),
):
return {
"category": category,
"price_range": f"${min_price} - ${max_price}",
}
输出:
INFO: 127.0.0.1:50123 - "GET /api/items HTTP/1.1" 200 OK
输出(
/prices?category=electronics&min_price=10&max_price=500):
{"category": "electronics", "price_range": "$10.0 - $500.0"}
(2) Query() 高级选项
| 选项 | 功能 | 示例 |
|---|---|---|
alias |
参数别名(如驼峰转蛇形) | Query(alias="minPrice") |
deprecated |
标记废弃 | Query(deprecated=True) |
title |
OpenAPI 标题 | Query(title="Category Filter") |
description |
OpenAPI 描述 | Query(description="...") |
examples |
示例值 | Query(examples=["electronics"]) |
▶ 示例:alias 和 deprecated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/products")
async def list_products(
sort_by: str = Query(
"name",
alias="sortBy",
description="Sort field: name, price, created_at",
),
old_filter: str | None = Query(
None,
deprecated=True,
description="Use sort_by instead",
),
):
return {"sort_by": sort_by}
输出:
# 函数定义成功
5. 枚举参数
(1) 字符串枚举限制可选值
当参数只能取固定几个值时,用 Enum 约束,FastAPI 自动在文档中展示下拉选择。
▶ 示例:枚举路径参数
from enum import Enum
from fastapi import FastAPI
class Category(str, Enum):
electronics = "electronics"
clothing = "clothing"
food = "food"
books = "books"
app = FastAPI()
@app.get("/categories/{category}")
async def get_category(category: Category):
return {
"category": category,
"value": category.value,
"label": category.name,
}
输出:
INFO: 127.0.0.1:50123 - "GET /api/items HTTP/1.1" 200 OK
输出(
/categories/electronics):
{"category": "electronics", "value": "electronics", "label": "electronics"}
▶ 示例:枚举查询参数与排序
from enum import Enum
from fastapi import FastAPI, Query
class SortOrder(str, Enum):
asc = "asc"
desc = "desc"
app = FastAPI()
@app.get("/products")
async def list_products(
sort_order: SortOrder = Query(SortOrder.asc),
limit: int = Query(20, ge=1, le=100),
offset: int = Query(0, ge=0),
):
return {
"sort": sort_order.value,
"limit": limit,
"offset": offset,
}
输出:
# 函数定义成功
6. 多参数组合与陷阱
(1) 参数声明顺序规则
FastAPI 对参数类型的判断规则:路径中存在的 {param} 是路径参数,否则是查询参数(有类型注解的是必选,有默认值的是可选)。
| 顺序 | 参数类型 | 判断依据 |
|---|---|---|
| 1 | 路径参数 | URL 中含 {param} |
| 2 | 查询参数(必选) | 无默认值,不在路径中 |
| 3 | 查询参数(可选) | 有默认值或 None |
▶ 示例:多参数组合——PriceTracker 商品查询
from fastapi import FastAPI, Path, Query
from enum import Enum
class Category(str, Enum):
electronics = "electronics"
clothing = "clothing"
food = "food"
app = FastAPI()
@app.get("/products/{product_id}/prices")
async def get_product_prices(
product_id: int = Path(gt=0, description="Product ID"),
category: Category | None = Query(None, description="Filter by category"),
min_price: float = Query(0.0, ge=0, description="Min price in USD"),
max_price: float = Query(99999.0, ge=0, description="Max price in USD"),
sort: str = Query("date", pattern="^(date|price)$"),
limit: int = Query(20, ge=1, le=100),
offset: int = Query(0, ge=0),
):
return {
"product_id": product_id,
"category": category,
"price_range": [min_price, max_price],
"sort": sort,
"pagination": {"limit": limit, "offset": offset},
}
输出:
# 函数定义成功
(2) 常见陷阱
| 陷阱 | 错误写法 | 正确写法 |
|---|---|---|
| 路径参数可选 | product_id: int = None |
路径参数必须必选 |
| 默认值与 Query 冲突 | limit: int = 20, Query(ge=1) |
limit: int = Query(20, ge=1) |
| 可选参数无 None | category: str = None |
`category: str |
| 枚举不用 str 基类 | class Cat(Enum): |
class Cat(str, Enum): |
7. 综合示例
路径参数、查询参数和枚举约束是构建灵活 API 的基础。下面整合 Path 约束、Query 分页与 Enum 筛选。
from fastapi import FastAPI, Path, Query
from enum import Enum
app = FastAPI()
class SortOrder(str, Enum):
asc = "asc"
desc = "desc"
PRODUCTS = [{"id": i, "name": f"Product-{i}", "price": i * 10.0} for i in range(1, 101)]
@app.get("/products/{product_id}")
async def get_product(
product_id: int = Path(gt=0, description="商品 ID"),
sort: SortOrder = Query(SortOrder.asc),
limit: int = Query(10, ge=1, le=100),
offset: int = Query(0, ge=0),
):
return {
"product_id": product_id,
"sort": sort.value,
"limit": limit,
"offset": offset,
}
输出:
GET /products/5?sort=desc&limit=20&offset=10 → {"product_id":5,"sort":"desc","limit":20,"offset":10}
GET /products/0 → 422 Validation Error (product_id must be > 0)
❓ 常见问题
category: str 是必选,category: str = "all" 是可选。也可用 Query(...) 显式标记必选。gt=0 表示必须 > 0(不含 0),ge=0 表示 >= 0(含 0)。ID 类参数一般用 gt=0,价格类用 ge=0。Query(max_length=N) 限制字符串长度,Query(ge=N, le=M) 限制数值范围。📖 小节
- 路径参数用
{param}声明在 URL 中,FastAPI 按类型提示自动转换和校验 Path()添加数值范围约束(gt/ge/lt/le)和字符串约束(min_length/max_length)- 查询参数用函数参数声明,有默认值可选、无默认值必选
Query()支持alias/deprecated/description/examples等高级选项- 枚举参数(
str, Enum)限制可选值,自动在文档中展示下拉选择
📝 作业
- 基础题(难度⭐):创建一个 GET 端点
/items/{item_id},要求item_id为正整数,返回{"item_id": item_id}。提示:item_id: int = Path(gt=0) - 进阶题(难度⭐⭐):创建 PriceTracker 的
/products查询端点,支持category(可选字符串,最长 50 字符)、min_price(≥0)和max_price(≤999999)三个查询参数。提示:Query(None, max_length=50) - 挑战题(难度⭐⭐⭐):创建一个
/products/{product_id}/prices端点,结合路径参数(product_id > 0)、枚举查询参数(SortOrder: asc/desc)、分页参数(limit 1-100,offset ≥ 0),验证非法输入返回 422。提示:定义SortOrder(str, Enum)和多个Query()
---|