跳到内容

请求体 - 多参数

现在我们已经了解了如何使用 PathQuery,接下来看看请求体声明的更多高级用法。

混合使用 PathQuery 和请求体参数

首先,当然可以自由混合 PathQuery 和请求体参数声明,FastAPI 会自动处理。

你还可以通过将默认值设为 None,将请求体参数声明为可选参数。

from typing import Annotated

from fastapi import FastAPI, Path
from pydantic import BaseModel

app = FastAPI()


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


@app.put("/items/{item_id}")
async def update_item(
    item_id: Annotated[int, Path(title="The ID of the item to get", ge=0, le=1000)],
    q: str | None = None,
    item: Item | None = None,
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    if item:
        results.update({"item": item})
    return results
🤓 其他版本和变体

提示

如果可能,请优先使用 Annotated 版本。

from fastapi import FastAPI, Path
from pydantic import BaseModel

app = FastAPI()


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


@app.put("/items/{item_id}")
async def update_item(
    *,
    item_id: int = Path(title="The ID of the item to get", ge=0, le=1000),
    q: str | None = None,
    item: Item | None = None,
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    if item:
        results.update({"item": item})
    return results

注意

请注意,在这种情况下,从请求体获取的 item 是可选的,因为它有一个 None 的默认值。

多个请求体参数

在前面的示例中,路径操作期望一个包含 Item 属性的 JSON 请求体,例如

{
    "name": "Foo",
    "description": "The pretender",
    "price": 42.0,
    "tax": 3.2
}

但你也可以声明多个请求体参数,例如 itemuser

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


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


class User(BaseModel):
    username: str
    full_name: str | None = None


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, user: User):
    results = {"item_id": item_id, "item": item, "user": user}
    return results

在这种情况下,FastAPI 会注意到函数中存在多个请求体参数(即两个 Pydantic 模型参数)。

因此,它会将参数名作为请求体中的键(字段名),并期望收到如下请求体:

{
    "item": {
        "name": "Foo",
        "description": "The pretender",
        "price": 42.0,
        "tax": 3.2
    },
    "user": {
        "username": "dave",
        "full_name": "Dave Grohl"
    }
}

注意

请注意,即使 item 的声明方式与之前相同,现在也要求将其置于以 item 为键的结构中。

FastAPI 会自动转换请求数据,使 item 参数接收其特定的内容,user 参数亦然。

它将执行组合数据的校验,并为 OpenAPI 模式和自动文档生成相应的文档。

请求体中的单值

正如 QueryPath 用于定义查询参数和路径参数的额外数据一样,FastAPI 也提供了对应的 Body

例如,在扩展前面的模型时,你可能决定除了 itemuser 之外,还在同一个请求体中包含另一个 importance 键。

如果你直接声明它,由于它是一个单值,FastAPI 会默认将其视为查询参数。

但你可以使用 Body 指示 FastAPI 将其视为另一个请求体键。

from typing import Annotated

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


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


class User(BaseModel):
    username: str
    full_name: str | None = None


@app.put("/items/{item_id}")
async def update_item(
    item_id: int, item: Item, user: User, importance: Annotated[int, Body()]
):
    results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
    return results
🤓 其他版本和变体

提示

如果可能,请优先使用 Annotated 版本。

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


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


class User(BaseModel):
    username: str
    full_name: str | None = None


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, user: User, importance: int = Body()):
    results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
    return results

在这种情况下,FastAPI 将期望收到如下请求体:

{
    "item": {
        "name": "Foo",
        "description": "The pretender",
        "price": 42.0,
        "tax": 3.2
    },
    "user": {
        "username": "dave",
        "full_name": "Dave Grohl"
    },
    "importance": 5
}

同样,它会自动转换数据类型、校验、生成文档等。

多个请求体参数与查询参数

当然,除了请求体参数外,你还可以根据需要声明额外的查询参数。

由于单值在默认情况下会被解析为查询参数,你无需显式添加 Query,直接编写即可:

q: str | None = None

例如

from typing import Annotated

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


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


class User(BaseModel):
    username: str
    full_name: str | None = None


@app.put("/items/{item_id}")
async def update_item(
    *,
    item_id: int,
    item: Item,
    user: User,
    importance: Annotated[int, Body(gt=0)],
    q: str | None = None,
):
    results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
    if q:
        results.update({"q": q})
    return results
🤓 其他版本和变体

提示

如果可能,请优先使用 Annotated 版本。

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


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


class User(BaseModel):
    username: str
    full_name: str | None = None


@app.put("/items/{item_id}")
async def update_item(
    *,
    item_id: int,
    item: Item,
    user: User,
    importance: int = Body(gt=0),
    q: str | None = None,
):
    results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
    if q:
        results.update({"q": q})
    return results

信息

Body 也具备与 QueryPath 等后续将学到的参数相同的额外校验和元数据参数。

嵌入单个请求体参数

假设你只有一个来自 Pydantic 模型 Itemitem 请求体参数。

默认情况下,FastAPI 会直接期望获取该模型的内容。

但如果你希望它像声明多个请求体参数时那样,期望一个包含 item 键的 JSON,并将模型内容放在该键内部,可以使用特殊的 Body 参数 embed

item: Item = Body(embed=True)

如下所示:

from typing import Annotated

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


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


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
    results = {"item_id": item_id, "item": item}
    return results
🤓 其他版本和变体

提示

如果可能,请优先使用 Annotated 版本。

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


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


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item = Body(embed=True)):
    results = {"item_id": item_id, "item": item}
    return results

在这种情况下,FastAPI 将期望收到如下请求体:

{
    "item": {
        "name": "Foo",
        "description": "The pretender",
        "price": 42.0,
        "tax": 3.2
    }
}

而不是:

{
    "name": "Foo",
    "description": "The pretender",
    "price": 42.0,
    "tax": 3.2
}

总结

你可以在路径操作函数中添加多个请求体参数,即使请求本身只能有一个请求体。

FastAPI 会处理它,为函数提供正确的数据,并在路径操作中校验并记录正确的架构。

你也可以声明将单值作为请求体的一部分接收。

即使只有一个参数声明,你也可以指示 FastAPI 将请求体嵌入到一个键中。