Skip to content

Body - 多個參數

🌐 AI 與人類共同完成的翻譯

此翻譯由人類指導的 AI 完成。🤝

可能會有對原意的誤解,或讀起來不自然等問題。🤖

你可以透過協助我們更好地引導 AI LLM來改進此翻譯。

英文版

現在我們已經知道如何使用 PathQuery,接下來看看更進階的請求主體(request body)宣告用法。

混用 PathQuery 與 Body 參數

首先,當然你可以自由混用 PathQuery 與請求 Body 參數的宣告,FastAPI 會知道該怎麼做。

你也可以將 Body 參數宣告為可選,方法是將預設值設為 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
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

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

注意

請注意,在此情況下,從 body 取得的 item 是可選的,因為它的預設值是 None

多個 Body 參數

在前一個範例中,路徑操作(path operation)會期望一個包含 Item 屬性的 JSON 主體,例如:

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

但你也可以宣告多個 Body 參數,例如 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 會注意到函式中有多個 Body 參數(有兩個參數是 Pydantic 模型)。

因此,它會使用參數名稱作為 body 中的鍵(欄位名稱),並期望如下的主體:

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

注意

儘管 item 的宣告方式與先前相同,現在預期它會位於 body 內,且鍵為 item

FastAPI 會自動從請求中進行轉換,讓參數 item 收到對應內容,user 亦同。

它會對複合資料進行驗證,並在 OpenAPI 結構與自動文件中如此描述。

Body 中的單一值

就像你可以用 QueryPath 為查詢與路徑參數定義額外資訊一樣,FastAPI 也提供對應的 Body

例如,延伸前述模型,你可以決定在相同的 Body 中,除了 itemuser 外,還要有另一個鍵 importance

如果直接這樣宣告,因為它是單一值,FastAPI 會將其視為查詢參數。

但你可以使用 Body 指示 FastAPI 將其視為另一個 Body 鍵:

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
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

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
}

同樣地,它會進行型別轉換、驗證、文件化等。

多個 Body 參數與 Query

當然,你也可以在任何 Body 參數之外,視需要宣告額外的查詢參數。

由於預設情況下,單一值會被解讀為查詢參數,你不必明確使用 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
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

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 以及之後你會看到的其他工具相同的額外驗證與中繼資料參數。

嵌入單一 Body 參數

假設你只有一個來自 Pydantic 模型 Item 的單一 item Body 參數。

預設情況下,FastAPI 會直接期望該模型的內容作為請求主體。

但如果你想讓它像宣告多個 Body 參數時那樣,期望一個帶有 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
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

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
}

小結

即便一個請求只能有單一主體,你仍可在你的路徑操作函式中宣告多個 Body 參數。

FastAPI 會處理好這一切,在你的函式中提供正確的資料,並在路徑操作中驗證與文件化正確的結構。

你也可以將單一值宣告為 Body 的一部分來接收。

即使只宣告了一個參數,也可以指示 FastAPI 將 Body 以某個鍵進行嵌入。