跳到内容

声明请求示例数据

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

以下是几种实现方式。

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

这些额外信息将原样添加到该模型的 JSON Schema 输出中,并用于 API 文档。

你可以使用 model_config 属性,它接受一个 dict,详情请参阅 Pydantic 文档:Configuration

你可以设置 "json_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

JSON Schema 中的 examples - OpenAPI

当使用以下任何一种方式时:

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

你还可以声明一组带有额外信息的 examples,这些信息将被添加到 OpenAPI 内的 JSON Schema 中。

带有 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
🤓 其他版本和变体

提示

如果可能,请优先使用 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

文档 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
🤓 其他版本和变体

提示

如果可能,请优先使用 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

当你这样做时,这些示例将成为该主体数据内部 JSON Schema 的一部分。

然而,在编写本文时,负责显示文档 UI 的工具 Swagger UI 并不支持在 JSON Schema 中显示多个数据示例。但请阅读下方的解决方法。

OpenAPI 特有的 examples

JSON Schema 支持 examples 之前,OpenAPI 就已经支持另一个也称为 examples 的字段。

这个 OpenAPI 特有的 examples 位于 OpenAPI 规范的另一个部分。它位于 每个路径操作(path operation)的详情 中,而不是在每个 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
🤓 其他版本和变体

提示

如果可能,请优先使用 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

文档 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 其他地方(如上所述)那样带有额外元数据的字典。

信息

即使在 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(它使用 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 或以上版本,一切都会变得更加 简单、一致且直观,你也不必了解所有这些历史细节。 😎