查询参数(Query Parameters) —— 这是 FastAPI 中另一个核心概念,用于处理 URL 中 ? 后面的键值对,比如分页、搜索、过滤等场景。

🧩 一、什么是查询参数?

查询参数出现在 URL 的 ? 之后,以 key=value 形式出现,多个参数用 & 连接:

GET /items/?skip=0&limit=10
GET /search?q=python&category=web
  • skip=0limit=10q=python 都是查询参数
  • 它们不是资源标识,而是操作选项(如分页、筛选、排序)

✅ 二、基本语法与示例

在 FastAPI 中,函数中未出现在路径 {} 中的参数,默认就是查询参数

🔹 示例 1:简单查询参数

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/")
def read_items(skip: int = 0, limit: int = 10):
    return {"skip": skip, "limit": limit}
  • 请求:GET /items/
    {"skip": 0, "limit": 10}(使用默认值)
  • 请求:GET /items/?skip=20&limit=5
    {"skip": 20, "limit": 5}
✅ 查询参数可选,通过设置默认值实现。

🔹 示例 2:字符串查询(如搜索)

@app.get("/search/")
def search(q: str = ""):
    if q:
        return {"results": [f"item_{q}_1", f"item_{q}_2"]}
    return {"results": []}
  • 请求:GET /search/?q=fastapi
    → 返回匹配结果
  • 请求:GET /search/
    → 返回空列表(因为 q=""

🔹 示例 3:布尔值、列表等类型

FastAPI 支持多种类型自动转换:

@app.get("/products/")
def list_products(
    in_stock: bool = True,
    tags: list[str] = None,      # Python 3.9+;旧版用 List[str]
):
    if tags is None:
        tags = []
    return {
        "in_stock": in_stock,
        "tags": tags
    }
  • 请求:GET /products/?in_stock=false&tags=electronics&tags=smart
    {"in_stock": False, "tags": ["electronics", "smart"]}
💡 多个同名参数(如 tags=...&tags=...)会被自动转为列表!

⚠️ 三、重要注意事项(新手易错点)

1️⃣ 查询参数必须有默认值(或使用 Optional

# ❌ 错误:没有默认值,FastAPI 会认为它是必需的路径参数!
@app.get("/items/")
def read_items(limit: int):  # 报错:找不到路径参数 {limit}

# ✅ 正确:给默认值 或 用 Optional
from typing import Optional
def read_items(limit: Optional[int] = None):
    ...
# 或(Python 3.10+)
def read_items(limit: int | None = None):
    ...

规则

  • 路径参数 → 出现在路径 {} 中 → 必需
  • 查询参数 → 不在路径中 → 必须有默认值(否则 FastAPI 无法区分)

2️⃣ 参数名大小写敏感,且需合法

URL 中参数名要和函数参数名一致:

def search(Query: str = ""):  # 参数名是 Query(大写 Q)

→ 必须请求:?Query=xxx,而不是 ?query=xxx


3️⃣ 不要和路径参数重名

@app.get("/users/{user_id}")
def read_user(user_id: int, user_id: str = "default"):  # ❌ 重复参数名!

这是语法错误!路径参数和查询参数不能同名。


4️⃣ 空值处理:None vs 空字符串

  • q: str = "" → 空字符串也是有效值
  • q: Optional[str] = None → 明确表示“未提供”

根据业务逻辑选择。


🛠 四、高级用法:使用 Query 进行更多控制

FastAPI 提供 Query 类,用于添加校验、文档说明、别名等。

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
def read_items(
    q: str = Query(
        default=None,
        min_length=3,
        max_length=50,
        title="Search Query",
        description="关键词,至少3个字符"
    ),
    skip: int = Query(0, ge=0),   # ge = greater than or equal
    limit: int = Query(10, le=100)  # le = less than or equal
):
    results = [{"item_id": "foo"}, {"item_id": "bar"}]
    if q:
        results = [{"item_id": q}]
    return {"results": results[:limit]}
  • 请求:GET /items/?q=hi → 返回 422 错误(长度不足)
  • 自动生成 OpenAPI 文档中的说明和限制!
✅ 推荐在需要输入校验API 文档优化时使用 Query

🔀 五、路径参数 + 查询参数 混合使用

非常常见!例如:获取某个用户的所有订单,并支持分页。

@app.get("/users/{user_id}/orders/")
def get_user_orders(
    user_id: int,           # ← 路径参数(必需)
    skip: int = 0,          # ← 查询参数(可选)
    limit: int = 10         # ← 查询参数(可选)
):
    return {
        "user_id": user_id,
        "orders": [f"order_{i}" for i in range(skip, skip + limit)]
    }
  • 请求:GET /users/123/orders/?skip=10&limit=5
  • 响应:包含 user_id=123 的订单列表,从第10条开始取5条

📊 六、路径参数 vs 查询参数 对比总结

特性路径参数查询参数
URL 位置/users/123/users/?id=123
是否必需✅ 是❌ 否(可设默认值)
语义标识资源(主键)操作选项(过滤、分页)
默认值不支持✅ 必须有(或用 Optional
类型校验支持支持
多值不支持✅ 支持(自动转列表)
OpenAPI 文档显示在路径中显示在“Parameters”下

✅ 七、最佳实践建议

  1. 资源定位用路径参数/users/{id}, /posts/{slug}
  2. 过滤/分页用查询参数?page=2&size=20, ?status=active
  3. 给查询参数设合理默认值:如 limit=10, skip=0
  4. Query 添加校验和文档:提升 API 可用性和安全性
  5. 避免过度使用查询参数:如果参数是资源核心标识,考虑改用路径参数

🎯 小练习(你可以试试看!)

写一个 API:

  • 路径:/books/{genre}/
  • 查询参数:

    • min_rating: float = 0.0(最小评分)
    • author: str = None(可选作者)
  • 返回模拟书籍列表
@app.get("/books/{genre}/")
def get_books_by_genre(
    genre: str,
    min_rating: float = 0.0,
    author: str | None = None
):
    books = [
        {"title": "Book A", "genre": genre, "rating": 4.5, "author": "Alice"},
        {"title": "Book B", "genre": genre, "rating": 3.8, "author": "Bob"},
    ]
    filtered = [
        b for b in books
        if b["rating"] >= min_rating and (author is None or b["author"] == author)
    ]
    return {"books": filtered}