作者学习fastapi文档时整理,欢迎各位查看
十一、声明请求示例数据
你可以为你的应用将接收的数据声明示例。
这里有几种实现方式。
1、Pydantic 模型中的额外 JSON Schema 数据
你可以为一个 Pydantic 模型声明examples,它们会被添加到生成的 JSON Schema 中。
fromfastapiimportFastAPIfrompydanticimportBaseModel app=FastAPI()classItem(BaseModel):name:strdescription:str|None=Noneprice:floattax:float|None=Nonemodel_config={"json_schema_extra":{"examples":[{"name":"Foo","description":"A very nice Item","price":35.4,"tax":3.2,}]}}@app.put("/items/{item_id}")asyncdefupdate_item(item_id:int,item:Item):results={"item_id":item_id,"item":item}returnresults这些额外信息会原样添加到该模型输出的JSON Schema中,并会在 API 文档中使用。
你可以使用属性model_config,它接收一个dict
你可以设置"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。
2、Field的附加参数
在 Pydantic 模型中使用Field()时,你也可以声明额外的examples:
fromfastapiimportFastAPIfrompydanticimportBaseModel,Field app=FastAPI()classItem(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}")asyncdefupdate_item(item_id:int,item:Item):results={"item_id":item_id,"item":item}returnresults3、JSON Schema 中的examples- OpenAPI
在以下任意场景中使用:
Path()Query()Header()Cookie()Body()Form()File()
你也可以声明一组examples,这些带有附加信息的示例将被添加到它们在OpenAPI中的JSON Schema里。
带有examples的Body
这里我们向Body()传入examples,其中包含一个期望的数据示例:
fromtypingimportAnnotatedfromfastapiimportBody,FastAPIfrompydanticimportBaseModel app=FastAPI()classItem(BaseModel):name:strdescription:str|None=Noneprice:floattax:float|None=None@app.put("/items/{item_id}")asyncdefupdate_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}returnresults其他版本:non-annotated
fromfastapiimportBody,FastAPIfrompydanticimportBaseModel app=FastAPI()classItem(BaseModel):name:strdescription:str|None=Noneprice:floattax:float|None=None@app.put("/items/{item_id}")asyncdefupdate_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}returnresults带有多个examples的Body
当然,你也可以传入多个examples:
fromtypingimportAnnotatedfromfastapiimportBody,FastAPIfrompydanticimportBaseModel app=FastAPI()classItem(BaseModel):name:strdescription:str|None=Noneprice:floattax:float|None=None@app.put("/items/{item_id}")asyncdefupdate_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}returnresults其他版本:non-annotated
fromfastapiimportBody,FastAPIfrompydanticimportBaseModel app=FastAPI()classItem(BaseModel):name:strdescription:str|None=Noneprice:floattax:float|None=None@app.put("/items/{item_id}")asyncdefupdate_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}returnresults这样做时,这些示例会成为该请求体数据内部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。
你可以这样使用:
fromtypingimportAnnotatedfromfastapiimportBody,FastAPIfrompydanticimportBaseModel app=FastAPI()classItem(BaseModel):name:strdescription:str|None=Noneprice:floattax:float|None=None@app.put("/items/{item_id}")asyncdefupdate_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}returnresults其他版本:
fromfastapiimportBody,FastAPIfrompydanticimportBaseModel app=FastAPI()classItem(BaseModel):name:strdescription:str|None=Noneprice:floattax:float|None=None@app.put("/items/{item_id}")asyncdefupdate_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}returnresults4、技术细节
- 注:
此部分针对于FastAPI版本 **0.99.0以下版本 **。
它们对更早版本(OpenAPI 3.1.0 尚不可用之前)更相关。
在 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参数,自 FastAPI0.103.0起改名为openapi_examples。
JSON Schema 的examples字段
后来,JSON Schema 在新版本的规范中添加了examples字段。
随后新的 OpenAPI 3.1.0 基于最新版本(JSON Schema 2020-12),其中包含了这个新的examples字段。
现在,这个新的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),当你在其他工具(Query()、Body()等)中使用example或examples时,这些示例不会被添加到描述该数据的 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
由于截至 2023-08-26,Swagger UI 不支持多个 JSON Schema 示例,用户无法在文档中展示多个示例。
为了解决这个问题,FastAPI0.103.0增加了支持,可以通过新参数openapi_examples声明同样的旧式OpenAPI 特定的examples字段。
十二、额外数据类型
我们一直在使用常见的数据类型,如:
intfloatstrbool
但是也可以使用更复杂的数据类型,仍然会拥有现在已经看到的相同的特性:
- 很棒的编辑器支持。
- 传入请求的数据转换。
- 响应数据转换。
- 数据验证。
- 自动注解和文档。
1、其他数据类型
下面是一些你可以使用的其他数据类型:
- UUID
- 一种标准的 “通用唯一标识符” ,在许多数据库和系统中用作ID。
- 在请求和响应中将以
str表示。
- datetime.datetime
- 一个 Python
datetime.datetime. - 在请求和响应中将表示为 ISO 8601 格式的
str,比如:2008-09-15T15:53:00+05:00.
- 一个 Python
- datetime.date
- Python
datetime.date. - 在请求和响应中将表示为 ISO 8601 格式的
str,比如:2008-09-15.
- Python
- datetime.time
- 一个 Python
datetime.time. - 在请求和响应中将表示为 ISO 8601 格式的
str,比如:14:23:55.003.
- 一个 Python
- datetime.timedelta
- 一个 Python
datetime.timedelta. - 在请求和响应中将表示为
float代表总秒数。 - Pydantic 也允许将其表示为 “ISO 8601 时间差异编码”, 查看文档了解更多信息。
- 一个 Python
- frozenset
- 在请求和响应中,作为 set对待:
- 在请求中,列表将被读取,消除重复,并将其转换为一个
set。 - 在响应中
set将被转换为list。 - 产生的模式将指定那些
set的值是唯一的 (使用 JSON Schema 的uniqueItems)。
- 在请求中,列表将被读取,消除重复,并将其转换为一个
- 在请求和响应中,作为 set对待:
- bytes
- 标准的 Python
bytes。 - 在请求和响应中被当作
str处理。 - 生成的模式将指定这个
str是binary“格式”。
- 标准的 Python
- Decimal
- 标准的 Python
Decimal。 - 在请求和响应中被当做
float一样处理。
- 标准的 Python
2、例
下面是一个路径操作的示例,其中的参数使用了上面的一些类型。
注意,函数内的参数有原生的数据类型,例如,你可以执行正常的日期操作
fromdatetimeimportdatetime,time,timedeltafromtypingimportAnnotatedfromuuidimportUUIDfromfastapiimportBody,FastAPI app=FastAPI()@app.put("/items/{item_id}")asyncdefread_items(item_id:UUID,start_datetime:Annotated[datetime,Body()],end_datetime:Annotated[datetime,Body()],process_after:Annotated[timedelta,Body()],repeat_at:Annotated[time|None,Body()]=None,):start_process=start_datetime+process_after duration=end_datetime-start_processreturn{"item_id":item_id,"start_datetime":start_datetime,"end_datetime":end_datetime,"process_after":process_after,"repeat_at":repeat_at,"start_process":start_process,"duration":duration,}其他版本:non_annotated
fromdatetimeimportdatetime,time,timedeltafromuuidimportUUIDfromfastapiimportBody,FastAPI app=FastAPI()@app.put("/items/{item_id}")asyncdefread_items(item_id:UUID,start_datetime:datetime=Body(),end_datetime:datetime=Body(),process_after:timedelta=Body(),repeat_at:time|None=Body(default=None),):start_process=start_datetime+process_after duration=end_datetime-start_processreturn{"item_id":item_id,"start_datetime":start_datetime,"end_datetime":end_datetime,"process_after":process_after,"repeat_at":repeat_at,"start_process":start_process,"duration":duration,}