FastAPI 教程

FastAPI 请求体

在 FastAPI 中,请求体(Request Body) 是客户端发送给服务器的数据,通常用于 POSTPUTPATCH 等请求,且默认格式为 JSON (application/json)。

FastAPI 处理请求体的核心武器是 Pydantic 模型。你只需定义数据的结构,FastAPI 就会自动完成:读取 JSON -> 类型转换 -> 数据验证 -> 生成 API 文档

以下是处理请求体的核心用法和进阶技巧:


1. 基础用法:定义 Pydantic 模型

首先,导入 BaseModel 并定义你的数据结构。然后,在路径操作函数中将其作为参数类型。

纯文本
plaintext
from typing import Annotated
from pydantic import BaseModel
from fastapi import FastAPI

app = FastAPI()

# 1. 定义数据模型
class Item(BaseModel):
    name: str
    description: str | None = None  # 可选字段
    price: float
    tax: float | None = None        # 可选字段

# 2. 在路由中使用该模型作为请求体
@app.post("/items/")
async def create_item(item: Item):
    # FastAPI 会自动验证传入的 JSON 是否符合 Item 模型
    # 如果符合,item 就是一个 Item 实例
    return {
        "item_name": item.name,
        "item_price": item.price,
        "item_dict": item.model_dump() # Pydantic V2 转换为字典的方法
    }

如果客户端发送 {"name": "Laptop", "price": 999.99},FastAPI 会自动处理。如果发送 {"price": "not_a_number"},FastAPI 会返回清晰的 422 验证错误。


2. 字段级验证 (使用 Field)

如果你需要对请求体中的某个字段添加更严格的规则(如最小长度、数值范围、正则表达式),可以使用 pydantic.Field

纯文本
plaintext
from pydantic import BaseModel, Field

class User(BaseModel):
    username: str = Field(min_length=3, max_length=50, description="用户登录名")
    age: int = Field(ge=18, le=120, description="年龄必须在 18 到 120 之间")
    email: str = Field(pattern=r'^[\w\.-]+@[\w\.-]+\.\w+$', description="必须是有效的邮箱")

@app.post("/users/")
async def create_user(user: User):
    return {"message": "User created", "user": user.model_dump()}

这些 description 还会自动同步到 Swagger UI (/docs) 中,极大提升 API 文档的质量。


3. 嵌套模型 (处理复杂 JSON)

Pydantic 模型可以作为其他模型的字段类型,完美支持嵌套的 JSON 结构。

纯文本
plaintext
from typing import List
from pydantic import BaseModel

class Image(BaseModel):
    url: str
    name: str

class Item(BaseModel):
    name: str
    price: float
    images: List[Image] | None = None  # 嵌套模型列表

@app.post("/items/complex/")
async def create_complex_item(item: Item):
    return item

客户端可以发送:{"name": "Phone", "price": 500, "images": [{"url": "http://...", "name": "front"}]}


4. 智能区分:路径参数 + 查询参数 + 请求体

FastAPI 最强大的特性之一是它能自动推断参数的来源。你可以在同一个函数中混合使用它们,FastAPI 绝不会混淆。

纯文本
plaintext
from typing import Annotated
from fastapi import FastAPI, Path, Query

app = FastAPI()

class Item(BaseModel):
    name: str
    price: float

@app.put("/items/{item_id}")
async def update_item(
    item_id: Annotated[int, Path(description="The ID of the item to update")], # 1. 路径参数
    q: Annotated[str | None, Query(description="Some query string")] = None,   # 2. 查询参数 (?q=...)
    item: Item,                                                                # 3. 请求体 (JSON)
):
    results = {"item_id": item_id, "item": item}
    if q:
        results.update({"q": q})
    return results

FastAPI 的规则:

  • 如果在路径中声明了(如 {item_id}),它就是路径参数
  • 如果是单一类型(str, int, float, bool 等),它默认是查询参数
  • 如果是 Pydantic 模型,它默认是请求体

5. 多个请求体参数 (使用 Body)

默认情况下,FastAPI 期望请求体是一个单一的 JSON 对象,其键对应你的 Pydantic 模型。
如果你在一个函数中声明了多个 Pydantic 模型作为参数,FastAPI 会期望请求体是一个包含这些模型名称作为键的字典。

为了让客户端发送标准的 JSON(而不是嵌套字典),你可以使用 Body(embed=True) 强制将参数嵌入到请求体的顶层,或者(更推荐的做法)将它们合并到一个大的 Pydantic 模型中

不推荐的做法(需要 embed=True):

纯文本
plaintext
from fastapi import Body

@app.post("/multi-body/")
async def multi_body(
    item: Item,
    user: User,
    # 强制 FastAPI 期望 JSON 格式为: {"item": {...}, "user": {...}}
    importance: Annotated[int, Body(embed=True, description="Priority level")] 
):
    return {"item": item, "user": user, "importance": importance}

✅ 推荐做法(合并模型):

纯文本
plaintext
class CreateOrderRequest(BaseModel):
    item: Item
    user: User
    importance: int = Field(description="Priority level")

@app.post("/orders/")
async def create_order(request: CreateOrderRequest):
    return request.model_dump()

将多个参数组合成一个 Request 模型,代码更清晰,且生成的 Swagger 文档更整洁。


6. 处理额外的未知字段

默认情况下,如果客户端在 JSON 中发送了 Pydantic 模型中未定义的字段,Pydantic 会直接忽略它们(在 V2 中这是默认行为,但明确声明是个好习惯)。

如果你想严格拒绝任何未定义的字段(提高安全性),可以配置模型:

纯文本
plaintext
from pydantic import BaseModel, ConfigDict

class StrictItem(BaseModel):
    model_config = ConfigDict(extra="forbid") # 拒绝额外字段

    name: str
    price: float

@app.post("/strict-items/")
async def create_strict_item(item: StrictItem):
    return item

如果客户端发送 {"name": "A", "price": 10, "extra_field": "oops"},FastAPI 将返回 422 错误。


总结:请求体最佳实践

  1. 始终使用 Pydantic 模型:不要尝试手动解析 request.json(),让 Pydantic 为你做验证和类型转换。
  2. 使用 AnnotatedField:将验证规则(如 min_length, gt)和文档描述(description)直接写在模型定义中。
  3. 复杂请求使用 Wrapper 模型:如果需要传递多个对象或额外元数据,创建一个包含所有字段的 XxxRequest 模型,而不是在路由函数中堆砌多个参数。
  4. 利用 /docs 测试:FastAPI 的 Swagger UI 会根据你的 Pydantic 模型自动生成完美的 JSON 请求体示例,直接在浏览器里点击 “Try it out” 即可测试。

接下来你想了解 FastAPI 的响应模型 (Response Model)(如何控制返回给客户端的数据),还是 异常处理 (Exception Handling)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注