FastAPI 入门教程FastAPI 入门教程
首页
基础教程
实战项目
FastAPI官网
首页
基础教程
实战项目
FastAPI官网
  • 基础教程

    • 📚 基础教程
    • 第0章 - Python 快速入门
    • 第1章 - FastAPI 简介
    • 第2章 - 环境搭建
    • 第3章 - 第一个 API
    • 第4章 - 路径参数
    • 第5章 - 查询参数
    • 第6章 - 请求体
    • 第7章 - 响应模型
    • 第8章 - CRUD 操作
    • 第9章 - 数据库操作

第5章 - 查询参数

嗨,朋友!我是长安。

这一章我们来讲查询参数。说实话,查询参数是我工作中用得最多的功能之一,像分页、搜索、筛选都靠它!

🎯 本章目标

  • 理解什么是查询参数
  • 学会定义必选和可选查询参数
  • 掌握查询参数的默认值
  • 学会使用 Query 进行验证

1️⃣ 什么是查询参数?

查询参数是 URL 中 ? 后面的部分,用 & 分隔多个参数。

比如:

  • /users?page=1 - page 是查询参数
  • /users?page=1&size=10 - page 和 size 都是查询参数
  • /search?keyword=python&sort=date - keyword 和 sort 是查询参数

2️⃣ 定义查询参数

在函数参数中,不属于路径参数的参数就是查询参数:

from fastapi import FastAPI

app = FastAPI()

@app.get("/items")
def get_items(page: int, size: int):
    return {
        "page": page,
        "size": size
    }

访问 http://127.0.0.1:8000/items?page=1&size=10,返回:

{"page": 1, "size": 10}

长安提醒

上面的例子中,page 和 size 都是必选参数,不传会报错! 这是很多新手容易忘记的点,我当年也经常犯这个错误。

3️⃣ 可选参数和默认值

设置默认值

@app.get("/items")
def get_items(page: int = 1, size: int = 10):
    return {"page": page, "size": size}

现在:

  • 访问 /items → {"page": 1, "size": 10}
  • 访问 /items?page=2 → {"page": 2, "size": 10}
  • 访问 /items?page=2&size=20 → {"page": 2, "size": 20}

可选参数(可以为 None)

from typing import Optional

@app.get("/search")
def search(keyword: Optional[str] = None):
    if keyword:
        return {"message": f"搜索:{keyword}"}
    return {"message": "请输入搜索关键词"}

或者使用 Python 3.10+ 的语法:

@app.get("/search")
def search(keyword: str | None = None):
    if keyword:
        return {"message": f"搜索:{keyword}"}
    return {"message": "请输入搜索关键词"}

4️⃣ 必选 vs 可选

from typing import Optional

@app.get("/users")
def get_users(
    page: int,                    # 必选,无默认值
    size: int = 10,               # 可选,有默认值
    keyword: Optional[str] = None # 可选,可以为 None
):
    return {
        "page": page,
        "size": size,
        "keyword": keyword
    }
参数是否必选说明
page: int必选没有默认值
size: int = 10可选有默认值 10
keyword: Optional[str] = None可选默认为 None

5️⃣ 路径参数 + 查询参数

可以同时使用路径参数和查询参数:

@app.get("/users/{user_id}/posts")
def get_user_posts(
    user_id: int,           # 路径参数
    page: int = 1,          # 查询参数
    size: int = 10          # 查询参数
):
    return {
        "user_id": user_id,
        "page": page,
        "size": size
    }

访问 /users/1/posts?page=2&size=5:

{"user_id": 1, "page": 2, "size": 5}

FastAPI 会自动区分:

  • 在路径中定义的 {user_id} → 路径参数
  • 其他参数 → 查询参数

6️⃣ 使用 Query 进行验证

使用 Query 可以对查询参数进行更详细的验证:

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items")
def get_items(
    page: int = Query(default=1, ge=1, description="页码"),
    size: int = Query(default=10, ge=1, le=100, description="每页数量")
):
    return {"page": page, "size": size}

Query 的常用参数

参数说明示例
default默认值default=1
ge大于等于ge=1
gt大于gt=0
le小于等于le=100
lt小于lt=100
min_length最小长度min_length=1
max_length最大长度max_length=50
pattern正则表达式pattern="^[a-z]+$"
title标题title="页码"
description描述description="当前页码"
example示例值example=1

字符串验证示例

@app.get("/search")
def search(
    keyword: str = Query(
        min_length=1,
        max_length=50,
        description="搜索关键词"
    )
):
    return {"keyword": keyword}

必选查询参数

使用 ... 表示必选:

@app.get("/search")
def search(
    keyword: str = Query(..., min_length=1, description="搜索关键词")
):
    return {"keyword": keyword}

... 是 Python 的 Ellipsis 对象,在这里表示"必须提供值"。

7️⃣ 接收列表参数

有时候需要接收多个相同名称的参数:

from typing import List

@app.get("/items")
def get_items(ids: List[int] = Query(default=[])):
    return {"ids": ids}

访问 /items?ids=1&ids=2&ids=3:

{"ids": [1, 2, 3]}

带默认值的列表

@app.get("/items")
def get_items(
    ids: List[int] = Query(default=[1, 2, 3])
):
    return {"ids": ids}

8️⃣ 参数别名

有时候前端传的参数名和 Python 变量名不一样(比如用连字符):

@app.get("/items")
def get_items(
    item_type: str = Query(alias="item-type")
):
    return {"item_type": item_type}

访问 /items?item-type=book:

{"item_type": "book"}

9️⃣ 废弃参数

标记某个参数即将废弃:

@app.get("/items")
def get_items(
    old_param: str = Query(default=None, deprecated=True),
    new_param: str = Query(default=None)
):
    return {"old": old_param, "new": new_param}

在文档中会显示该参数已废弃。

🔟 完整示例

from typing import List, Optional
from fastapi import FastAPI, Query

app = FastAPI(title="查询参数示例")

# 模拟数据
fake_items = [
    {"id": 1, "name": "苹果", "price": 5.0, "category": "水果"},
    {"id": 2, "name": "香蕉", "price": 3.0, "category": "水果"},
    {"id": 3, "name": "牛奶", "price": 8.0, "category": "饮品"},
    {"id": 4, "name": "面包", "price": 6.0, "category": "食品"},
    {"id": 5, "name": "可乐", "price": 4.0, "category": "饮品"},
]

@app.get("/items", tags=["商品"])
def get_items(
    page: int = Query(default=1, ge=1, description="页码"),
    size: int = Query(default=10, ge=1, le=100, description="每页数量"),
    keyword: Optional[str] = Query(default=None, min_length=1, description="搜索关键词"),
    category: Optional[str] = Query(default=None, description="商品分类"),
    min_price: Optional[float] = Query(default=None, ge=0, description="最低价格"),
    max_price: Optional[float] = Query(default=None, ge=0, description="最高价格"),
):
    """
    获取商品列表
    
    支持分页、搜索、分类筛选、价格筛选
    """
    result = fake_items.copy()
    
    # 关键词搜索
    if keyword:
        result = [item for item in result if keyword in item["name"]]
    
    # 分类筛选
    if category:
        result = [item for item in result if item["category"] == category]
    
    # 价格筛选
    if min_price is not None:
        result = [item for item in result if item["price"] >= min_price]
    if max_price is not None:
        result = [item for item in result if item["price"] <= max_price]
    
    # 分页
    start = (page - 1) * size
    end = start + size
    paginated = result[start:end]
    
    return {
        "total": len(result),
        "page": page,
        "size": size,
        "data": paginated
    }

@app.get("/items/batch", tags=["商品"])
def get_items_batch(
    ids: List[int] = Query(description="商品ID列表")
):
    """批量获取商品"""
    result = [item for item in fake_items if item["id"] in ids]
    return {"items": result}

@app.get("/search", tags=["搜索"])
def search(
    q: str = Query(..., min_length=1, max_length=100, description="搜索内容"),
    type: str = Query(default="all", description="搜索类型"),
    sort: str = Query(default="relevance", description="排序方式")
):
    """搜索接口"""
    return {
        "query": q,
        "type": type,
        "sort": sort,
        "results": []
    }

# 路径参数 + 查询参数
@app.get("/categories/{category}/items", tags=["分类"])
def get_category_items(
    category: str,
    page: int = Query(default=1, ge=1),
    size: int = Query(default=10, ge=1, le=50)
):
    """获取分类下的商品"""
    result = [item for item in fake_items if item["category"] == category]
    return {
        "category": category,
        "total": len(result),
        "page": page,
        "size": size,
        "data": result
    }

📝 小结

本章我们学习了:

  1. ✅ 查询参数的定义方式
  2. ✅ 必选参数和可选参数
  3. ✅ 默认值的设置
  4. ✅ 使用 Query 进行验证
  5. ✅ 接收列表参数
  6. ✅ 参数别名

🏃 下一步

现在你会处理 GET 请求的参数了,但是 POST 请求通常需要在请求体中传递数据。下一章学习请求体!

👉 第6章 - 请求体

💪 练习题

  1. 创建一个用户列表接口 /users,支持以下查询参数:

    • page: 页码,默认1,最小1
    • size: 每页数量,默认10,范围1-100
    • name: 用户名搜索,可选
    • status: 用户状态,可选
  2. 创建一个搜索接口 /search,要求:

    • keyword: 必选,长度1-50
    • type: 可选,默认"all"
    • page: 可选,默认1
  3. 创建一个批量查询接口 /users/batch,接收多个用户ID

参考答案
from typing import List, Optional
from fastapi import FastAPI, Query

app = FastAPI()

# 练习1
@app.get("/users")
def get_users(
    page: int = Query(default=1, ge=1),
    size: int = Query(default=10, ge=1, le=100),
    name: Optional[str] = Query(default=None),
    status: Optional[str] = Query(default=None)
):
    return {
        "page": page,
        "size": size,
        "name": name,
        "status": status
    }

# 练习2
@app.get("/search")
def search(
    keyword: str = Query(..., min_length=1, max_length=50),
    type: str = Query(default="all"),
    page: int = Query(default=1)
):
    return {
        "keyword": keyword,
        "type": type,
        "page": page
    }

# 练习3
@app.get("/users/batch")
def get_users_batch(
    ids: List[int] = Query(description="用户ID列表")
):
    return {"ids": ids}
最近更新: 2025/12/26 11:25
Contributors: 王长安
Prev
第4章 - 路径参数
Next
第6章 - 请求体