Skip to content

请求体

当你需要从客户端(比如浏览器)向你的API发送数据时,你可以将其作为**请求体**发送。

**请求体**是客户端发送给你的API的数据。**响应体**是你的API发送给客户端的数据。

你的API几乎总是需要发送**响应体**。但客户端并不总是需要发送**请求体**,有时它们只请求一个路径,可能带有一些查询参数,但并不发送请求体。

要声明一个**请求体**,你可以使用Pydantic模型,充分利用其功能和优势。

/// 信息

要发送数据,你应该使用以下方法之一:POST(更常见)、PUTDELETEPATCH

在规范中,发送带有GET请求的请求体的行为是未定义的,尽管如此,FastAPI支持这种做法,仅限于非常复杂或极端的使用场景。

由于不推荐这种做法,Swagger UI的交互式文档在使用GET时不会显示请求体的文档,中间的代理可能也不支持这种做法。

///

导入Pydantic的BaseModel

首先,你需要从pydantic导入BaseModel

//// 标签 | Python 3.10+

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

////

//// 标签 | Python 3.8+

from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


app = FastAPI()


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

////

创建你的数据模型

然后,你可以将你的数据模型声明为一个继承自BaseModel的类。

为所有属性使用标准的Python类型:

//// 标签 | Python 3.10+

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

////

//// 标签 | Python 3.8+

from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


app = FastAPI()


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

////

与声明查询参数时一样,当模型属性具有默认值时,它不是必需的。否则,它是必需的。使用None使其成为可选的。

例如,上面的模型声明了一个JSON“对象”(或Python dict),如下所示:

{
    "name": "Foo",
    "description": "An optional description",
    "price": 45.2,
    "tax": 3.5
}

...由于descriptiontax是可选的(默认值为None),这个JSON“对象”也是有效的:

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

将其声明为参数

要将其添加到你的*路径操作*中,你可以像声明路径和查询参数一样进行声明:

//// 标签 | Python 3.10+

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

////

//// 标签 | Python 3.8+

from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


app = FastAPI()


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

////

...并将其类型声明为你创建的模型Item

结果

仅通过这一Python类型声明,**FastAPI**将:

  • 读取请求体作为JSON。
  • 转换相应的类型(如果需要)。
  • 验证数据。
    • 如果数据无效,它将返回一个清晰且详细的错误,指出具体哪里和什么数据不正确。
  • 在参数item中提供接收到的数据。
    • 由于你在函数中将其声明为Item类型,你还将获得所有属性和其类型的编辑器支持(自动补全等)。
  • 为你的模型生成JSON Schema定义,你也可以在项目的其他地方使用这些定义,如果它们对你的项目有意义的话。
  • 这些模式将成为生成的OpenAPI模式的一部分,并被自动文档UI使用。

自动文档

你的模型的JSON模式将成为你生成的OpenAPI模式的一部分,并显示在交互式API文档中:

它们还将在每个需要它们的*路径操作*的API文档中使用:

编辑器支持

在你的编辑器中,在你的函数内部,你将在任何地方获得类型提示和自动补全(如果你接收的是dict而不是Pydantic模型,则不会发生这种情况):

你还会得到对不正确类型操作的错误检查:

这不是偶然的,整个框架都是围绕这种设计构建的。

在实施之前,它在设计阶段经过了彻底的测试,以确保它能在所有编辑器中工作。

甚至还有一些变化是针对Pydantic本身来支持这一点的。

之前的截图是在Visual Studio Code中拍摄的。

但你会在PyCharm和大多数其他Python编辑器中获得相同的编辑器支持:

/// 提示 如果你使用 PyCharm 作为你的编辑器,你可以使用 Pydantic PyCharm 插件

它增强了编辑器对 Pydantic 模型的支持,具有以下功能:

  • 自动补全
  • 类型检查
  • 重构
  • 搜索
  • 检查

///

使用模型

在函数内部,你可以直接访问模型对象的所有属性:

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.dict()
    if item.tax:
        price_with_tax = item.price + item.tax
        item_dict.update({"price_with_tax": price_with_tax})
    return item_dict
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


app = FastAPI()


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

请求体 + 路径参数

你可以同时声明路径参数和请求体。

FastAPI 会识别出与路径参数匹配的函数参数应该**从路径中获取**,而声明为 Pydantic 模型的函数参数应该**从请求体中获取**。

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.dict()}
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[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.dict()}

请求体 + 路径 + 查询参数

你也可以同时声明**请求体**、**路径**和**查询**参数。

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, q: str | None = None):
    result = {"item_id": item_id, **item.dict()}
    if q:
        result.update({"q": q})
    return result
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


app = FastAPI()


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

函数参数将被识别如下:

  • 如果参数也在**路径**中声明,它将被用作路径参数。
  • 如果参数是**单一类型**(如 intfloatstrbool 等),它将被解释为**查询**参数。
  • 如果参数被声明为**Pydantic 模型**类型,它将被解释为请求**体**。

Note

FastAPI 会知道 q 的值不是必需的,因为它的默认值是 = None

str | None(Python 3.10+)或 UnionUnion[str, None](Python 3.8+)中不被 FastAPI 用来确定该值不是必需的,它会知道它不是必需的,因为它有一个默认值 = None

但添加类型注解将允许你的编辑器提供更好的支持并检测错误。

不使用 Pydantic

如果你不想使用 Pydantic 模型,你也可以使用 Body 参数。请参阅文档 Body - 多参数:请求体中的单一值