跳到内容

声明请求示例数据

你可以声明你的应用可以接收的数据示例。

以下是几种实现方法。

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 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 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

    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 v2 中,你将使用属性 model_config,它接受一个 `dict`,如 Pydantic 文档:配置 中所述。

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

在 Pydantic v1 中,你将使用内部类 Configschema_extra,如 Pydantic 文档:Schema 定制 中所述。

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

提示

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

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

信息

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,其中包含额外信息,这些信息将添加到 OpenAPI 内的 JSON Schemas 中。

带有 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

提示

如果可能,请优先使用 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 的一部分。

然而,在撰写本文时,负责显示文档 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 参数

你可以在 FastAPI 中使用 openapi_examples 参数声明 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 版本起支持 🎉)。

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

Pydantic 和 FastAPI examples

当你在 Pydantic 模型内部添加 examples 时,无论是使用 schema_extra 还是 Field(examples=["something"]),该示例都会添加到该 Pydantic 模型的 JSON Schema 中。

并且该 Pydantic 模型的 JSON Schema 会被包含在你的 API 的 OpenAPI 中,然后在文档 UI 中使用。

在 FastAPI 0.99.0 之前的版本中(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,它使用 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 或更高版本**,一切都会变得**更简单、更一致、更直观**,你也不必了解所有这些历史细节。😎