宣告請求範例資料¶
您可以宣告應用程式可以接收的資料範例。
以下是幾種方法。
Pydantic 模型中的額外 JSON Schema 資料¶
您可以為 Pydantic 模型宣告 examples
,這些範例將會新增到產生的 JSON Schema 中。
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
model_config = {
"json_schema_extra": {
"examples": [
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
]
}
}
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
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 Config:
schema_extra = {
"examples": [
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
]
}
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
model_config = {
"json_schema_extra": {
"examples": [
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
]
}
}
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
class Config:
schema_extra = {
"examples": [
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
]
}
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
該額外資訊將會按原樣新增到該模型的輸出 **JSON Schema** 中,並將用於 API 文件中。
在 Pydantic 版本 2 中,您將使用屬性 model_config
,它接受一個 dict
作為參數,如 Pydantic 的文件:設定 中所述。
您可以使用包含您想要顯示在產生的 JSON Schema 中的任何額外資料(包括 examples
)的 dict
來設定 "json_schema_extra"
。
在 Pydantic 版本 1 中,您將使用內部類別 Config
和 schema_extra
,如 Pydantic 的文件:Schema 自訂 中所述。
您可以使用包含您想要顯示在產生的 JSON Schema 中的任何額外資料(包括 examples
)的 dict
來設定 schema_extra
。
提示
您可以使用相同的技巧來擴展 JSON Schema 並新增您自己的自訂額外資訊。
例如,您可以使用它來新增前端使用者介面的中繼資料等。
資訊
OpenAPI 3.1.0(自 FastAPI 0.99.0 起使用)新增了對 examples
的支援,這是 **JSON Schema** 標準的一部分。
在此之前,它僅支援帶有單個範例的關鍵字 example
。OpenAPI 3.1.0 仍然支援它,但已棄用,並且不是 JSON Schema 標準的一部分。因此,建議您將 example
遷移到 examples
。🤓
您可以在本頁末尾閱讀更多資訊。
Field
額外參數¶
當使用 Field()
與 Pydantic 模型時,您也可以宣告額外的 examples
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str = Field(examples=["Foo"])
description: str | None = Field(default=None, examples=["A very nice Item"])
price: float = Field(examples=[35.4])
tax: float | None = Field(default=None, examples=[3.2])
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str = Field(examples=["Foo"])
description: Union[str, None] = Field(default=None, examples=["A very nice Item"])
price: float = Field(examples=[35.4])
tax: Union[float, None] = Field(default=None, examples=[3.2])
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
JSON Schema 中的 examples
- OpenAPI¶
當使用以下任何一項時
Path()
Query()
Header()
Cookie()
Body()
Form()
File()
您也可以宣告一組帶有額外資訊的 examples
,這些資訊將會新增到 **OpenAPI** 內的 **JSON Schemas** 中。
帶有 examples
的 Body
¶
這裡我們傳遞包含 Body()
中預期資料的一個範例的 examples
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(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
],
),
],
):
results = {"item_id": item_id, "item": item}
return results
from typing import Annotated, Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(
item_id: int,
item: Annotated[
Item,
Body(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
],
),
],
):
results = {"item_id": item_id, "item": item}
return results
from typing import Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
from typing_extensions import Annotated
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(
item_id: int,
item: Annotated[
Item,
Body(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
],
),
],
):
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(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
],
),
):
results = {"item_id": item_id, "item": item}
return results
提示
如果可能,建議使用 Annotated
版本。
from typing import Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(
item_id: int,
item: Item = Body(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
],
),
):
results = {"item_id": item_id, "item": item}
return results
文件 UI 範例¶
使用上述任何方法,在 /docs
中看起來會像這樣
具有多個 examples
的 Body
¶
您當然也可以傳遞多個 examples
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(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
{
"name": "Bar",
"price": "35.4",
},
{
"name": "Baz",
"price": "thirty five point four",
},
],
),
],
):
results = {"item_id": item_id, "item": item}
return results
from typing import Annotated, Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Annotated[
Item,
Body(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
{
"name": "Bar",
"price": "35.4",
},
{
"name": "Baz",
"price": "thirty five point four",
},
],
),
],
):
results = {"item_id": item_id, "item": item}
return results
from typing import Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
from typing_extensions import Annotated
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Annotated[
Item,
Body(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
{
"name": "Bar",
"price": "35.4",
},
{
"name": "Baz",
"price": "thirty five point four",
},
],
),
],
):
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(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
{
"name": "Bar",
"price": "35.4",
},
{
"name": "Baz",
"price": "thirty five point four",
},
],
),
):
results = {"item_id": item_id, "item": item}
return results
提示
如果可能,建議使用 Annotated
版本。
from typing import Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Item = Body(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
{
"name": "Bar",
"price": "35.4",
},
{
"name": "Baz",
"price": "thirty five point four",
},
],
),
):
results = {"item_id": item_id, "item": item}
return results
當您這樣做時,這些範例將成為該主體資料內部 JSON Schema 的一部分。
然而,在撰寫本文時 (2023 年 8 月 26 日),負責顯示文件 UI 的工具 Swagger UI 不支援顯示 JSON Schema 中資料的多個範例。但請閱讀下文以了解解決方法。
OpenAPI 特定的 examples
¶
在 JSON Schema 支援 examples
之前,OpenAPI 就支援另一個也稱為 examples
的欄位。
這個 OpenAPI 特定的 examples
位於 OpenAPI 規範中的另一個區段。它位於每個路徑操作的詳細資訊中,而不是在每個 JSON Schema 內。
Swagger UI 也支援這個特定的 examples
欄位一段時間了。因此,您可以使用它來在文件 UI 中顯示不同的範例。
這個 OpenAPI 特定的欄位 examples
的形狀是一個帶有多個範例的 dict
(而不是 list
),每個範例都包含額外的資訊,這些資訊也會添加到 OpenAPI 中。
這不會放在 OpenAPI 中包含的每個 JSON Schema 內,而是放在外部,直接放在路徑操作中。
使用 openapi_examples
參數¶
您可以使用 openapi_examples
參數在 FastAPI 中宣告 OpenAPI 特定的 examples
,用於
Path()
Query()
Header()
Cookie()
Body()
Form()
File()
dict
的鍵識別每個範例,每個值是另一個 dict
。
examples
中的每個特定範例 dict
可以包含
summary
:範例的簡短描述。description
:可以包含 Markdown 文字的詳細描述。value
:這是顯示的實際範例,例如dict
。externalValue
:value
的替代方案,指向範例的 URL。雖然這可能不像value
那樣受到許多工具支援。
您可以像這樣使用它
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(
openapi_examples={
"normal": {
"summary": "A normal example",
"description": "A **normal** item works correctly.",
"value": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
},
"converted": {
"summary": "An example with converted data",
"description": "FastAPI can convert price `strings` to actual `numbers` automatically",
"value": {
"name": "Bar",
"price": "35.4",
},
},
"invalid": {
"summary": "Invalid data is rejected with an error",
"value": {
"name": "Baz",
"price": "thirty five point four",
},
},
},
),
],
):
results = {"item_id": item_id, "item": item}
return results
from typing import Annotated, Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Annotated[
Item,
Body(
openapi_examples={
"normal": {
"summary": "A normal example",
"description": "A **normal** item works correctly.",
"value": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
},
"converted": {
"summary": "An example with converted data",
"description": "FastAPI can convert price `strings` to actual `numbers` automatically",
"value": {
"name": "Bar",
"price": "35.4",
},
},
"invalid": {
"summary": "Invalid data is rejected with an error",
"value": {
"name": "Baz",
"price": "thirty five point four",
},
},
},
),
],
):
results = {"item_id": item_id, "item": item}
return results
from typing import Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
from typing_extensions import Annotated
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Annotated[
Item,
Body(
openapi_examples={
"normal": {
"summary": "A normal example",
"description": "A **normal** item works correctly.",
"value": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
},
"converted": {
"summary": "An example with converted data",
"description": "FastAPI can convert price `strings` to actual `numbers` automatically",
"value": {
"name": "Bar",
"price": "35.4",
},
},
"invalid": {
"summary": "Invalid data is rejected with an error",
"value": {
"name": "Baz",
"price": "thirty five point four",
},
},
},
),
],
):
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(
openapi_examples={
"normal": {
"summary": "A normal example",
"description": "A **normal** item works correctly.",
"value": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
},
"converted": {
"summary": "An example with converted data",
"description": "FastAPI can convert price `strings` to actual `numbers` automatically",
"value": {
"name": "Bar",
"price": "35.4",
},
},
"invalid": {
"summary": "Invalid data is rejected with an error",
"value": {
"name": "Baz",
"price": "thirty five point four",
},
},
},
),
):
results = {"item_id": item_id, "item": item}
return results
提示
如果可能,建議使用 Annotated
版本。
from typing import Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Item = Body(
openapi_examples={
"normal": {
"summary": "A normal example",
"description": "A **normal** item works correctly.",
"value": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
},
"converted": {
"summary": "An example with converted data",
"description": "FastAPI can convert price `strings` to actual `numbers` automatically",
"value": {
"name": "Bar",
"price": "35.4",
},
},
"invalid": {
"summary": "Invalid data is rejected with an error",
"value": {
"name": "Baz",
"price": "thirty five point four",
},
},
},
),
):
results = {"item_id": item_id, "item": item}
return results
文件 UI 中的 OpenAPI 範例¶
將 openapi_examples
添加到 Body()
後,/docs
看起來會像
技術細節¶
提示
如果您已經使用 FastAPI 版本 0.99.0 或更高版本,則可以跳過這些細節。
它們與 OpenAPI 3.1.0 可用之前的舊版本更相關。
您可以將此視為簡短的 OpenAPI 和 JSON Schema 歷史課程。🤓
警告
這些是關於標準 JSON Schema 和 OpenAPI 的非常技術性的細節。
如果上述想法已經對您有效,那可能就足夠了,您可能不需要這些細節,可以隨時跳過它們。
在 OpenAPI 3.1.0 之前,OpenAPI 使用的是較舊且經過修改的 JSON Schema 版本。
JSON Schema 沒有 examples
,因此 OpenAPI 將自己的 example
欄位添加到其修改的版本中。
OpenAPI 也將 example
和 examples
欄位添加到規範的其他部分
- FastAPI 使用的
參數物件
(在規範中)Path()
Query()
Header()
Cookie()
- FastAPI 使用的
請求主體物件
,在content
欄位中,位於媒體類型物件
上(在規範中)Body()
File()
Form()
資訊
這個舊的 OpenAPI 特有的 examples
參數,自 FastAPI 0.103.0
版本起,現在改為 openapi_examples
。
JSON Schema 的 examples
欄位¶
但後來 JSON Schema 在新版本的規範中加入了 examples
欄位。
接著,新的 OpenAPI 3.1.0 是基於包含這個新欄位 examples
的最新版本 (JSON Schema 2020-12)。
現在,這個新的 examples
欄位優先於舊的單個(且客製化的)example
欄位,而後者現在已被棄用。
JSON Schema 中這個新的 examples
欄位**只是一個 list
**,也就是範例的列表,不像 OpenAPI 其他地方(如上所述)那樣是一個帶有額外元資料的 dict。
資訊
即使在 OpenAPI 3.1.0 發布並採用這種與 JSON Schema 更簡潔的整合方式之後,提供自動文件工具的 Swagger UI 有一段時間仍不支援 OpenAPI 3.1.0(直到 5.0.0 版才開始支援 🎉)。
因此,0.99.0 之前的 FastAPI 版本仍然使用低於 3.1.0 的 OpenAPI 版本。
Pydantic 和 FastAPI 的 examples
¶
當您使用 schema_extra
或 Field(examples=["something"])
在 Pydantic 模型中加入 examples
時,該範例會被添加到該 Pydantic 模型的**JSON Schema** 中。
而 Pydantic 模型的**JSON Schema** 會被包含在您的 API 的**OpenAPI** 中,然後用於文件 UI。
在 0.99.0 之前的 FastAPI 版本中(0.99.0 及以上版本使用較新的 OpenAPI 3.1.0),當您使用 example
或 examples
與其他任何工具(Query()
、Body()
等)時,這些範例不會被添加到描述該資料的 JSON Schema 中(甚至不會添加到 OpenAPI 自己的 JSON Schema 版本中),它們會直接添加到 OpenAPI 的*路徑操作*宣告中(在 OpenAPI 使用 JSON Schema 的部分之外)。
但現在 FastAPI 0.99.0 及以上版本使用 OpenAPI 3.1.0,而 OpenAPI 3.1.0 使用 JSON Schema 2020-12,加上 Swagger UI 5.0.0 及以上版本,一切都更加一致,範例也被包含在 JSON Schema 中。
Swagger UI 和 OpenAPI 特有的 examples
¶
現在,由於 Swagger UI 不支援多個 JSON Schema 範例(截至 2023-08-26),使用者無法在文件中顯示多個範例。
為了克服這個問題,FastAPI 0.103.0
**新增了支援**,可以使用新的參數 openapi_examples
來宣告與舊版相同的**OpenAPI 特有** examples
欄位。🤓
總結¶
我以前常說我不太喜歡歷史...看看我現在竟然在上「技術史」課。😅
簡而言之,**升級到 FastAPI 0.99.0 或更高版本**,事情就會變得更**簡單、一致且直觀**,您也不必了解所有這些歷史細節。😎