声明请求示例数据¶
你可以声明你的应用可以接收的数据示例。
以下是几种实现方法。
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 中,你将使用内部类 Config
和 schema_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 中。
带有 examples
的 Body
¶
这里我们传入 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
中的显示效果将如下所示:
带有多个 examples
的 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,
},
{
"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
。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
提示
如果可能,请优先使用 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 还在规范的其他部分添加了 example
和 examples
字段:
Parameter Object
(在规范中) 被 FastAPI 的...使用Path()
Query()
Header()
Cookie()
Request Body Object
,在content
字段中,位于Media Type Object
(在规范中) 被 FastAPI 的...使用Body()
File()
Form()
信息
这个旧的 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 或更高版本**,一切都会变得**更简单、更一致、更直观**,你也不必了解所有这些历史细节。😎