Skip to content

声明请求示例数据

您可以声明您的应用程序可以接收的数据示例。

以下是几种实现方法。

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_extra" 为一个包含您希望在生成的 JSON Schema 中显示的任何额外数据的 dict,包括 examples

在 Pydantic 版本 1 中,您将使用内部类 Configschema_extra,如 Pydantic 文档:Schema 定制 中所述。

您可以设置 schema_extra 为一个包含您希望在生成的 JSON Schema 中显示的任何额外数据的 dict,包括 examples

Tip

您可以使用相同的技术扩展 JSON Schema 并添加您自己的自定义额外信息。

例如,您可以使用它为前端用户界面添加元数据等。

Info

OpenAPI 3.1.0(自 FastAPI 0.99.0 起使用)增加了对 examples 的支持,这是 JSON Schema 标准的一部分。

在此之前,它仅支持带有单个示例的关键字 example。这仍然被 OpenAPI 3.1.0 支持,但已被弃用,并且不是 JSON Schema 标准的一部分。因此,建议您将 example 迁移到 examples。🤓

您可以在本页末尾阅读更多信息。

Field 的额外参数

在使用 Pydantic 模型时,您还可以通过 Field() 声明额外的 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 - OpenAPI 中的 examples

在使用以下任意一项时:

  • Path()
  • Query()
  • Header()
  • Cookie()
  • Body()
  • Form()
  • File()

您还可以声明一组 examples,其中包含将添加到其 JSON Schemas 中的额外信息,这些信息位于 OpenAPI 内部。

带有 examplesBody

在这里,我们传递 examples,其中包含 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


@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

Tip

如果可能,建议使用 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

Tip

如果可能,建议使用 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

Tip

如果可能,建议使用 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

Tip

如果可能,建议使用 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

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日),负责展示文档界面的工具 Swagger UI 并不支持在 JSON Schema 中显示多个示例。但请继续阅读以了解解决方法。

OpenAPI 特有的 examples

JSON Schema 支持 examples 之前,OpenAPI 已经支持了一个名为 examples 的不同字段。

这个 OpenAPI 特有的 examples 位于 OpenAPI 规范的另一个部分。它位于每个 路径操作 的详细信息中,而不是在每个 JSON Schema 内部。

Swagger UI 已经支持这个特定的 examples 字段有一段时间了。因此,你可以使用它来在文档界面中 展示 不同的 示例

这个 OpenAPI 特有的字段 examples 的结构是一个包含 多个示例dict(而不是 list),每个示例都带有额外的信息,这些信息也会被添加到 OpenAPI 中。

这不会放在 OpenAPI 中包含的每个 JSON Schema 内部,而是直接放在 路径操作 的外部。

使用 openapi_examples 参数

你可以在 FastAPI 中使用 openapi_examples 参数来声明 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

Tip

如果可能,建议使用 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

Tip

如果可能,建议使用 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

文档界面中的 OpenAPI 示例

Body() 中添加了 openapi_examples 后,/docs 界面将如下所示:

技术细节

Tip

如果你已经在使用 FastAPI 版本 0.99.0 或更高版本,你可能可以 跳过 这些细节。

这些细节对于旧版本更为相关,尤其是在 OpenAPI 3.1.0 可用之前。

你可以将其视为 OpenAPI 和 JSON Schema 的简短 历史课。🤓

Warning

以下是关于 JSON SchemaOpenAPI 标准的非常技术性的细节。

如果上面的想法已经对你有效,那可能已经足够了,你可能不需要这些细节,可以随意跳过。

在 OpenAPI 3.1.0 之前,OpenAPI 使用的是旧版且经过修改的 JSON Schema

JSON Schema 没有 examples,因此 OpenAPI 在其修改版本中添加了自己的 example 字段。

OpenAPI 还在规范的其他部分添加了 exampleexamples 字段:

Info

这个旧的 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 字段 只是一个示例列表,不像 OpenAPI 中其他地方(如上所述)那样是一个带有额外元数据的 dict

Info

即使在OpenAPI 3.1.0发布并引入了与JSON Schema更简单的集成之后,一段时间内,提供自动文档的工具Swagger UI并不支持OpenAPI 3.1.0(自版本5.0.0起支持了🎉)。

因此,FastAPI在0.99.0之前的版本仍然使用低于3.1.0的OpenAPI版本。

Pydantic和FastAPI的examples

当你在Pydantic模型内部添加examples时,使用schema_extraField(examples=["something"]),该示例会被添加到该Pydantic模型的**JSON Schema**中。

而这个Pydantic模型的**JSON Schema**会被包含在你的API的**OpenAPI**中,然后在文档UI中使用。

在FastAPI 0.99.0之前的版本(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,该版本使用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或更高版本,事情会变得更加**简单、一致和直观**,你不需要了解所有这些历史细节。😎