首页 / FastAPI 入门教程 / 请求体 Request Body 与 Pydantic 入门

FastAPI 入门教程

请求体 Request Body 与 Pydantic 入门

本教程共 50 篇 · 第 10 篇 · 更新于 2026-08-12 · 约 7 分钟阅读

FastAPIFastAPI 入门教程请求体PydanticBaseModelPOST

本节目标:学会用 Pydantic 的 BaseModel 定义数据结构,通过 @app.post 接收客户端发来的 JSON 请求体,并理解 FastAPI 如何自动完成校验、转换和文档生成。

前面我们学过路径参数和查询参数。它们都写在网址里,适合传递少量、简单的数据。可当你要提交一整条商品信息、一份注册表单时,靠网址就不现实了。

这时候就要用到「请求体」。请求体是客户端发给服务器的主体数据,通常是一段 JSON。FastAPI 借助 Pydantic 来处理它,整个过程几乎是自动的。

10-1 请求体是什么

请求体是客户端发送给接口的数据。相对地,响应体是接口返回给客户端的数据。

大部分接口都要返回响应体,但客户端并不总需要发请求体。比如打开一个页面,往往只用路径或查询参数就够了。一旦要「提交数据」,就要用请求体。

发送请求体时,应当使用 POST(最常见)、PUTDELETEPATCH。用 GET 带请求体虽然技术上可行,但不符合规范,Swagger 文档也不会显示它,所以不要这么干。

Note

JSON 是请求体最常用的格式。它长得像 Python 的字典:用花括号包裹,键和字符串用双引号。FastAPI 默认就把请求体当成 JSON 来解析。

10-2 Pydantic 是什么

Pydantic 是 FastAPI 的核心依赖,负责数据校验和转换。它的思路很直观:用 Python 的类型注解描述数据结构,剩下的校验、报错、转类型都交给它。

在 FastAPI 里,Pydantic 主要做四件事:

  • 校验客户端发来的 JSON 是否符合要求。
  • 把数据转换成你声明的类型(比如字符串变浮点数)。
  • 把模型结构自动写进 API 文档。
  • 让你在编辑器里获得属性补全和类型提示。

你不需要自己写一堆判断逻辑。只要声明一次类型,Pydantic 就把脏活累活全包了。

10-3 用 BaseModel 定义模型

定义请求体,先要从 pydantic 导入 BaseModel,再写一个继承它的类。类里的每个属性,就是请求体里的一个字段。

from fastapi import FastAPI
from pydantic import BaseModel

class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None

app = FastAPI()

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

这个 Item 模型描述了一个商品:name 是名称,price 是价格,descriptiontax 是可选的。

字段是否必填,只看有没有默认值。写了默认值(比如 = None)就是可选的;没写默认值就是必填的。所以 nameprice 必填,descriptiontax 可以不传。

下面这段 JSON 都是合法的请求体:

{
    "name": "Foo",
    "description": "一份可选的描述",
    "price": 45.2,
    "tax": 3.5
}

即使省掉可选字段,只留必填项,同样能通过校验:

{
    "name": "Foo",
    "price": 45.2
}
Tip

我们用了 str | None 这种写法,这是 Python 3.10 起的联合类型语法。它等同于旧写法 Optional[str],但更简洁。本教程统一使用新写法。

10-4 用 @app.post 接收请求体

把模型作为参数写进路径操作函数,FastAPI 就知道要去请求体里取数据。注意参数类型要写你定义的模型名 Item

上面那段代码里,item: Item 这个声明就是关键。FastAPI 看到参数类型是 Pydantic 模型,就会自动把它当请求体处理。

启动服务用的是 FastAPI 自带的命令行工具:

fastapi dev main.py

等价地,你也可以用 Uvicorn 启动:

uvicorn main:app --reload

打开浏览器访问 http://127.0.0.1:8000/docs,你会看到一个交互式文档页面。在 /items/ 这个接口里,能直接看到 Item 的结构,还能点「Try it out」填数据测试。

10-5 字段类型即校验

只靠那一行类型声明,FastAPI 就替你做了下面这些事:

  • 把请求体按 JSON 读进来。
  • 把数据转换成声明的类型(比如把字符串 "45.2" 转成浮点数)。
  • 校验数据是否合法。
  • 不合法时,返回清晰的报错,精确指出哪个字段出了错。
  • 把校验后的数据放进 item 参数,你还能获得编辑器补全。
  • 自动生成 JSON Schema,并融进 OpenAPI 文档。

举个例子,如果客户端把 price 传成字符串 "abc",FastAPI 会直接拒绝,并返回类似这样的错误:

{
    "detail": [
        {
            "type": "float_parsing",
            "loc": ["body", "price"],
            "msg": "Input should be a valid number",
            "input": "abc"
        }
    ]
}

loc 告诉你出错位置在请求体的 price 字段,msg 说明原因。这种报错对前端调试非常友好。

10-6 在函数中访问模型属性

进了函数内部,你可以像操作普通 Python 对象一样,直接访问模型的属性。

from fastapi import FastAPI
from pydantic import BaseModel

class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None

app = FastAPI()

@app.post("/items/")
async def create_item(item: Item):
    item_dict = item.model_dump()
    if item.tax:
        price_with_tax = item.price + item.tax
        item_dict.update({"price_with_tax": price_with_tax})
    return item_dict

这里用到了 item.model_dump(),它把模型转成一个普通字典。这是 Pydantic v2 的写法,用来替代旧版的 .dict()

我们还顺手算了一个含税价格 price_with_tax,加进返回的字典里。注意访问属性时用的是 item.priceitem.tax,就像访问对象字段一样自然。

10-7 请求体搭配路径参数

请求体和路径参数可以同时出现。FastAPI 很聪明,能分辨哪个从路径取、哪个从请求体取。

from fastapi import FastAPI
from pydantic import BaseModel

class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None

app = FastAPI()

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    return {"item_id": item_id, **item.model_dump()}

item_id 在路径 {item_id} 里出现,所以是路径参数;item 的类型是 Item 模型,所以是请求体。两者互不干扰。

10-8 三者混用时的识别规则

请求体、路径参数、查询参数能一起用。FastAPI 按下面规则自动判断数据来源:

  • 参数名出现在路径的 {} 里 → 路径参数。
  • 参数是单一类型(intstrfloatbool 等)→ 查询参数。
  • 参数类型是 Pydantic 模型 → 请求体。
from fastapi import FastAPI
from pydantic import BaseModel

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

app = FastAPI()

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, q: str | None = None):
    result = {"item_id": item_id, **item.model_dump()}
    if q:
        result.update({"q": q})
    return result

上面的函数里,item_id 来自路径,q 来自查询字符串,item 来自请求体。FastAPI 会把它们各归各位。

Note

q 之所以被当成可选的查询参数,是因为它有默认值 = None。类型注解 str | None 本身不影响是否必填,真正起作用的是「有没有默认值」。

10-9 小结

请求体是提交复杂数据的主要方式,用 @app.post 接收最合适。定义它的核心是 Pydantic 的 BaseModel

  • 用标准 Python 类型声明字段,有默认值就可选,没默认值就必填。
  • 把模型写成函数参数,FastAPI 自动完成 JSON 解析、类型转换和数据校验。
  • 校验失败会返回带精确位置的报错,方便排查。
  • 模型结构自动进入 /docs 文档,无需手写。
  • 在函数中用 .model_dump()(Pydantic v2)把模型转成字典。

掌握请求体,你就具备了构建真实接口的基础能力。下一章我们进一步给字段加上更细的约束。