跳至內容

宣告請求範例資料

您可以宣告應用程式可以接收的資料範例。

以下是幾種方法。

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 中,您將使用內部類別 Configschema_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** 中。

帶有 examplesBody

這裡我們傳遞包含 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 中看起來會像這樣

具有多個 examplesBody

您當然也可以傳遞多個 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
  • externalValuevalue 的替代方案,指向範例的 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 SchemaOpenAPI 的非常技術性的細節。

如果上述想法已經對您有效,那可能就足夠了,您可能不需要這些細節,可以隨時跳過它們。

在 OpenAPI 3.1.0 之前,OpenAPI 使用的是較舊且經過修改的 JSON Schema 版本。

JSON Schema 沒有 examples,因此 OpenAPI 將自己的 example 欄位添加到其修改的版本中。

OpenAPI 也將 exampleexamples 欄位添加到規範的其他部分

資訊

這個舊的 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_extraField(examples=["something"]) 在 Pydantic 模型中加入 examples 時,該範例會被添加到該 Pydantic 模型的**JSON Schema** 中。

而 Pydantic 模型的**JSON Schema** 會被包含在您的 API 的**OpenAPI** 中,然後用於文件 UI。

在 0.99.0 之前的 FastAPI 版本中(0.99.0 及以上版本使用較新的 OpenAPI 3.1.0),當您使用 exampleexamples 與其他任何工具(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 或更高版本**,事情就會變得更**簡單、一致且直觀**,您也不必了解所有這些歷史細節。😎