请求体、查询参数与 Pydantic 模型
💡 本部分目标:学会接收用户通过 URL 传递的查询参数,以及通过 JSON 发送的请求体数据;掌握使用 Pydantic 定义和验证数据结构。
✅ 一、回顾:什么是"请求"?
当客户端(如浏览器、手机 App、Postman)向你的 API 发起请求时,可能携带以下信息:
| 类型 | 示例 | 用途 |
|---|---|---|
| 路径参数 | /users/123 中的 123 |
标识特定资源 |
| 查询参数 | /search?q=python&page=2 中的 q=python |
用于过滤、分页等 |
| 请求体(Body) | POST 请求中的 JSON 数据 | 提交复杂数据(如注册表单) |
第1部分我们学了路径参数 ,本部分重点学习查询参数 和请求体。
✅ 二、查询参数(Query Parameters)
查询参数出现在 URL 的 ? 后面,用 & 分隔,例如:
/items/?skip=0&limit=10
/users?active=true&role=admin
如何在 FastAPI 中接收查询参数?
只需在函数中添加带默认值的参数,FastAPI 会自动将其识别为查询参数。
示例 1:简单查询参数
python
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/")
def read_items(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}
访问:
- http://127.0.0.1:8000/items/ → 返回
{"skip": 0, "limit": 10} - http://127.0.0.1:8000/items/?skip=5\&limit=20 → 返回
{"skip": 5, "limit": 20}
🔍 注意:因为
skip和limit有默认值(= 0,= 10),所以是可选 的查询参数。如果没有默认值(如
q: str),则该参数是必填的。
示例 2:必填 vs 可选查询参数
python
@app.get("/search/")
def search(q: str, category: str = None):
result = {"query": q}
if category:
result["category"] = category
return result
- ✅ 必须提供
q:/search/?q=fastapi - ❌ 缺少
q会报错(422 Validation Error) - ✅
category是可选的
✅ 三、请求体(Request Body)
当需要提交复杂数据(如用户注册信息),通常使用 POST 请求 + JSON 请求体。
FastAPI 使用 Pydantic 模型 来定义和验证请求体结构。
什么是 Pydantic?
Pydantic 是一个基于 Python 类型提示的数据验证库。FastAPI 内置支持它。
步骤 1:定义数据模型
python
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None # 可选字段(Python 3.10+ 语法)
price: float
tax: float | None = None
💡 如果你使用的是 Python < 3.10,可以用:
pythonfrom typing import Optional description: Optional[str] = None
步骤 2:在路由中使用模型
python
@app.post("/items/")
def create_item(item: Item):
return item
测试请求体
使用 Swagger UI(http://127.0.0.1:8000/docs):
-
点击
/items/→ "POST" -
点击 "Try it out"
-
输入 JSON:
json{ "name": "笔记本电脑", "price": 5999.99, "tax": 599.99 } -
点击 "Execute"
你会收到相同的 JSON 响应!
✅ FastAPI 自动:
- 解析 JSON
- 验证字段类型(如
price必须是数字)- 转换为
Item对象- 如果数据无效,返回 422 错误(含详细错误信息)
✅ 四、同时使用路径参数、查询参数和请求体
你可以混合使用所有类型的参数!
python
@app.put("/items/{item_id}")
def update_item(
item_id: int, # 路径参数
q: str | None = None, # 查询参数(可选)
item: Item # 请求体
):
result = {"item_id": item_id, "item": item.dict()}
if q:
result["q"] = q
return result
⚠️ 注意:请求体必须是 Pydantic 模型,而路径/查询参数是普通类型。
✅ 五、为什么使用 Pydantic 模型?
- 自动验证:确保数据符合预期格式
- 自动生成文档:Swagger UI 会显示每个字段的类型和是否可选
- 类型安全:IDE 可以提供智能提示
- 数据转换 :比如字符串
"123"自动转为整数123(如果字段是int)
✅ 六、完整示例代码(推荐保存为 main.py)
python
# main.py
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional # 如果使用 Python < 3.10
app = FastAPI(title="第2部分:请求体与查询参数")
# 定义数据模型
class UserCreate(BaseModel):
username: str
email: str
age: int
is_active: bool = True # 默认值
# 查询参数示例
@app.get("/products/")
def list_products(
category: Optional[str] = None,
min_price: float = 0.0,
max_results: int = 10
):
products = [
{"name": "手机", "price": 3999, "category": "电子"},
{"name": "书", "price": 59, "category": "教育"}
]
# 简单过滤(实际项目中会用数据库)
filtered = [
p for p in products
if p["price"] >= min_price and (not category or p["category"] == category)
]
return {"products": filtered[:max_results]}
# 请求体示例
@app.post("/users/")
def create_user(user: UserCreate):
return {
"message": f"用户 {user.username} 已创建!",
"user": user.dict()
}
# 混合使用
@app.post("/items/{item_id}/reviews")
def add_review(
item_id: int,
review_text: str, # 查询参数(评论内容)
rating: int = 5, # 查询参数(评分,默认5)
reviewer: UserCreate # 请求体(评论者信息)
):
return {
"item_id": item_id,
"review": {
"text": review_text,
"rating": rating,
"by": reviewer.username
}
}
运行后,在 /docs 中尝试:
- GET
/products/?category=电子&min_price=1000 - POST
/users/发送 JSON 用户数据 - POST
/items/123/reviews?review_text=很好用!&rating=4并附带 reviewer 的 JSON
✅ 七、练习任务(动手实践)
🧠 请先自己尝试完成,再查看下方答案!
任务1:创建一个搜索图书的接口
- 路由:
GET /books/ - 支持两个可选 查询参数:
author(字符串)min_year(整数,默认为 0)
- 返回示例:
{"books": [{"title": "Python入门", "author": "张三", "year": 2022}]}
任务2:实现用户注册接口
- 路由:
POST /register/ - 请求体包含:
username(字符串)password(字符串)email(字符串)
- 返回注册成功的消息和用户名
任务3(挑战):更新商品信息
- 路由:
PUT /products/{product_id} - 路径参数:
product_id(整数) - 查询参数:
include_details(布尔值,默认False) - 请求体:包含
name(字符串)、price(浮点数) - 如果
include_details=true,在响应中额外返回"updated_at": "2026-01-12"
✅ 八、练习任务参考答案
✅ 任务1 答案
python
@app.get("/books/")
def search_books(author: str = None, min_year: int = 0):
fake_books = [
{"title": "Python编程", "author": "李四", "year": 2020},
{"title": "FastAPI实战", "author": "王五", "year": 2023},
{"title": "数据科学", "author": "李四", "year": 2021}
]
result = [
book for book in fake_books
if (not author or book["author"] == author) and book["year"] >= min_year
]
return {"books": result}
测试:
/books/?author=李四&min_year=2021
✅ 任务2 答案
python
class RegisterUser(BaseModel):
username: str
password: str
email: str
@app.post("/register/")
def register(user: RegisterUser):
return {
"message": "注册成功!",
"username": user.username
}
测试(在 Swagger 中):
json
{
"username": "alice",
"password": "secret123",
"email": "alice@example.com"
}
✅ 任务3 答案
python
from datetime import date
class ProductUpdate(BaseModel):
name: str
price: float
@app.put("/products/{product_id}")
def update_product(
product_id: int,
include_details: bool = False,
product: ProductUpdate = None
):
response = {
"product_id": product_id,
"name": product.name,
"price": product.price
}
if include_details:
response["updated_at"] = str(date.today()) # 如 "2026-01-12"
return response
💡 注意:
product: ProductUpdate = None不推荐,更好的写法是直接product: ProductUpdate(因为 PUT 通常需要完整数据)。这里仅为演示。
✅ 九、小结
在本部分,你学会了:
- 使用查询参数接收 URL 中的过滤条件
- 使用 Pydantic 模型 定义和验证请求体
- 混合使用路径参数、查询参数和请求体
- 利用 FastAPI 自动生成的文档测试复杂接口