跳到内容

请求体

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

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

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

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

信息

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

在规范中,发送带有 GET 请求的请求体是未定义行为,尽管如此,FastAPI 也支持它,但这仅限于非常复杂/极端的用例。

由于不推荐这样做,Swagger UI 的交互式文档不会显示 GET 请求体的文档,并且中间的代理可能不支持它。

导入 Pydantic 的 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
🤓 其他版本和变体
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 类型

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
🤓 其他版本和变体
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
}

将其声明为一个参数

要将其添加到你的*路径操作*中,只需按照声明路径和查询参数的方式进行声明即可。

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
🤓 其他版本和变体
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 Schema 将构成你的 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.model_dump()
    if item.tax is not None:
        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.model_dump()
    if item.tax is not None:
        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.model_dump()}
🤓 其他版本和变体
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.model_dump()}

请求体 + 路径 + 查询参数

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

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.model_dump()}
    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.model_dump()}
    if q:
        result.update({"q": q})
    return result

函数参数将识别如下:

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

注意

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

str | None(Python 3.10+)或 Union[str, None] 中的 Union(Python 3.9+)不是 FastAPI 用来确定值是否必需的,它之所以知道值不是必需的,是因为它有一个默认值 = None

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

不使用 Pydantic

如果你不想使用 Pydantic 模型,你也可以使用Body 参数。请参阅 Body - Multiple Parameters: Singular values in body 的文档。