跳到内容

声明请求示例数据

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

这里有几种方法可以做到。

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

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

您可以使用 model_config 属性,该属性接受一个 dict,如 Pydantic 文档:配置 中所述。

您可以使用包含任何您希望显示在生成的 JSON Schema 中的额外数据的 dict 来设置 "json_schema_extra",包括 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 中的 examples - OpenAPI

在使用任何以下方法时:

  • 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

提示

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

提示

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

提示

如果可能,请优先使用 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 还向规范的其他部分添加了 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

当您使用 schema_extraField(examples=["something"]) 在 Pydantic 模型中添加 examples 时,该示例将被添加到该 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 或更高版本**,事情会变得更加 **简单、一致和直观**,您无需了解所有这些历史细节。😎