Body - 多個參數¶
現在我們已經知道如何使用 Path 與 Query,接下來看看更進階的請求主體(request body)宣告用法。
混用 Path、Query 與 Body 參數¶
首先,當然你可以自由混用 Path、Query 與請求 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 參數,例如 item 與 user:
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 中的單一值¶
就像你可以用 Query 與 Path 為查詢與路徑參數定義額外資訊一樣,FastAPI 也提供對應的 Body。
例如,延伸前述模型,你可以決定在相同的 Body 中,除了 item 與 user 外,還要有另一個鍵 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 也具有與 Query、Path 以及之後你會看到的其他工具相同的額外驗證與中繼資料參數。
嵌入單一 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 以某個鍵進行嵌入。