FastAPI: 路径参数与查询参数 — 精准路由设计

最后更新:2026-08-26

路由设计就像城市道路规划——路径参数是门牌号(精准定位),查询参数是筛选条件(缩小范围),两者配合才能快速找到目标。

1. 你将学到


2. Alice 的真实故事

(1) 痛点:商品查询 API 参数混乱

Alice 的 PriceTracker 需要支持多种查询方式:按商品 ID 精确查询、按品类和价格区间筛选、按排序方式分页浏览。Bob 前端传了 category=electronics&min_price=10&max_price=999,但 Alice 的 Flask 代码里手动解析每个参数,类型转换容易出错,负数价格和非法排序字段没有校验,线上事故频发。

(2) FastAPI 参数校验的解法

FastAPI 用类型提示自动解析和校验参数,Path()Query() 提供声明式约束,参数不合法自动返回 422 错误。

PYTHON
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 自动按类型提示转换。

100%
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 / 的字符串

▶ 示例:基础路径参数与类型转换

PYTHON
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))}

输出:

TEXT 📖 仅展示
INFO:     127.0.0.1:50123 - "GET /api/items HTTP/1.1" 200 OK

输出(/products/42):

TEXT 📖 仅展示
{"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() 数值约束

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

输出:

TEXT 📖 仅展示
INFO:     127.0.0.1:50123 - "GET /api/items HTTP/1.1" 200 OK

输出(/products/-1 返回 422):

TEXT 📖 仅展示
{
  "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`

▶ 示例:查询参数基础

PYTHON
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}",
    }

输出:

TEXT 📖 仅展示
INFO:     127.0.0.1:50123 - "GET /api/items HTTP/1.1" 200 OK

输出(/prices?category=electronics&min_price=10&max_price=500):

TEXT 📖 仅展示
{"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

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

输出:

TEXT 📖 仅展示
# 函数定义成功

5. 枚举参数

(1) 字符串枚举限制可选值

当参数只能取固定几个值时,用 Enum 约束,FastAPI 自动在文档中展示下拉选择。

▶ 示例:枚举路径参数

PYTHON
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,
    }

输出:

TEXT 📖 仅展示
INFO:     127.0.0.1:50123 - "GET /api/items HTTP/1.1" 200 OK

输出(/categories/electronics):

TEXT 📖 仅展示
{"category": "electronics", "value": "electronics", "label": "electronics"}

▶ 示例:枚举查询参数与排序

PYTHON
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,
    }

输出:

TEXT 📖 仅展示
# 函数定义成功

6. 多参数组合与陷阱

(1) 参数声明顺序规则

FastAPI 对参数类型的判断规则:路径中存在的 {param} 是路径参数,否则是查询参数(有类型注解的是必选,有默认值的是可选)。

顺序 参数类型 判断依据
1 路径参数 URL 中含 {param}
2 查询参数(必选) 无默认值,不在路径中
3 查询参数(可选) 有默认值或 None

▶ 示例:多参数组合——PriceTracker 商品查询

PYTHON
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},
    }

输出:

TEXT 📖 仅展示
# 函数定义成功

(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 筛选。

PYTHON
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,
    }

输出:

TEXT 📖 仅展示
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)

❓ 常见问题

Q 路径参数和查询参数能同名吗?
A 不能。FastAPI 会报错,因为同名参数无法区分来源。
Q 查询参数怎么设为必选?
A 不给默认值即可,如 category: str 是必选,category: str = "all" 是可选。也可用 Query(...) 显式标记必选。
Q 422 错误太详细会不会暴露信息?
A 开发阶段详细错误有帮助。生产环境可自定义异常处理器简化响应,只返回"参数校验失败"。
Q Enum 参数能接收小写吗?
A 默认区分大小写。如果要不区分,需自定义校验器或在 Enum 值中用小写。
Q Path() 的 gt 和 ge 有什么区别?
A gt=0 表示必须 > 0(不含 0),ge=0 表示 >= 0(含 0)。ID 类参数一般用 gt=0,价格类用 ge=0
Q 如何限制查询参数的长度?
AQuery(max_length=N) 限制字符串长度,Query(ge=N, le=M) 限制数值范围。

📖 小节


📝 作业

  1. 基础题(难度⭐):创建一个 GET 端点 /items/{item_id},要求 item_id 为正整数,返回 {"item_id": item_id}。提示:item_id: int = Path(gt=0)
  2. 进阶题(难度⭐⭐):创建 PriceTracker 的 /products 查询端点,支持 category(可选字符串,最长 50 字符)、min_price(≥0)和 max_price(≤999999)三个查询参数。提示:Query(None, max_length=50)
  3. 挑战题(难度⭐⭐⭐):创建一个 /products/{product_id}/prices 端点,结合路径参数(product_id > 0)、枚举查询参数(SortOrder: asc/desc)、分页参数(limit 1-100,offset ≥ 0),验证非法输入返回 422。提示:定义 SortOrder(str, Enum) 和多个 Query()

---|

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏