OpenAPI中的额外响应¶
/// 警告
这是一个相当高级的主题。
如果你刚开始使用 FastAPI,可能不需要这个。
///
你可以声明额外的响应,包括额外的状态码、媒体类型、描述等。
这些额外的响应将被包含在OpenAPI模式中,因此它们也会出现在API文档中。
但对于这些额外的响应,你必须确保直接返回一个 Response
对象,如 JSONResponse
,并带有你的状态码和内容。
带有 model
的额外响应¶
你可以向你的 路径操作装饰器 传递一个 responses
参数。
它接收一个 dict
,键是每个响应的状态码,如 200
,值是包含每个响应信息的另一个 dict
。
每个响应 dict
可以有一个键 model
,包含一个 Pydantic 模型,就像 response_model
一样。
FastAPI 将采用该模型,生成其 JSON Schema,并将其包含在 OpenAPI 中的正确位置。
例如,要声明一个带有状态码 404
和 Pydantic 模型 Message
的额外响应,你可以这样写:
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from pydantic import BaseModel
class Item(BaseModel):
id: str
value: str
class Message(BaseModel):
message: str
app = FastAPI()
@app.get("/items/{item_id}", response_model=Item, responses={404: {"model": Message}})
async def read_item(item_id: str):
if item_id == "foo":
return {"id": "foo", "value": "there goes my hero"}
return JSONResponse(status_code=404, content={"message": "Item not found"})
/// 注意
请记住,你必须直接返回 JSONResponse
。
///
/// 信息
model
键不是 OpenAPI 的一部分。
FastAPI 将从那里获取 Pydantic 模型,生成 JSON Schema,并将其放在正确的位置。
正确的位置是:
- 在键
content
中,其值是另一个 JSON 对象(dict
),包含:- 一个带有媒体类型的键,例如
application/json
,其值是另一个 JSON 对象,包含:- 一个键
schema
,其值是来自模型的 JSON Schema,这里是正确的位置。- FastAPI 在这里添加了对全局 JSON Schemas 的引用,而不是直接包含它。这样,其他应用程序和客户端可以直接使用这些 JSON Schemas,提供更好的代码生成工具等。
- 一个键
- 一个带有媒体类型的键,例如
///
此 路径操作 在 OpenAPI 中生成的响应将是:
{
"responses": {
"404": {
"description": "Additional Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Message"
}
}
}
},
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Item"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
这些模式被引用到 OpenAPI 模式中的另一个位置:
{
"components": {
"schemas": {
"Message": {
"title": "消息",
"required": [
"message"
],
"type": "object",
"properties": {
"message": {
"title": "消息",
"type": "string"
}
}
},
"Item": {
"title": "项目",
"required": [
"id",
"value"
],
"type": "object",
"properties": {
"id": {
"title": "ID",
"type": "string"
},
"value": {
"title": "值",
"type": "string"
}
}
},
"ValidationError": {
"title": "验证错误",
"required": [
"loc",
"msg",
"type"
],
"type": "object",
"properties": {
"loc": {
"title": "位置",
"type": "array",
"items": {
"type": "string"
}
},
"msg": {
"title": "消息",
"type": "string"
},
"type": {
"title": "错误类型",
"type": "string"
}
}
},
"HTTPValidationError": {
"title": "HTTP验证错误",
"type": "object",
"properties": {
"detail": {
"title": "详情",
"type": "array",
"items": {
"$ref": "#/components/schemas/ValidationError"
}
}
}
}
}
}
}
主响应的附加媒体类型¶
你可以使用相同的 responses
参数为相同的主响应添加不同的媒体类型。
例如,你可以添加一个额外的媒体类型 image/png
,声明你的 路径操作 可以返回一个 JSON 对象(使用媒体类型 application/json
)或一个 PNG 图像:
from typing import Union
from fastapi import FastAPI
from fastapi.responses import FileResponse
from pydantic import BaseModel
class Item(BaseModel):
id: str
value: str
app = FastAPI()
@app.get(
"/items/{item_id}",
response_model=Item,
responses={
200: {
"content": {"image/png": {}},
"description": "Return the JSON item or an image.",
}
},
)
async def read_item(item_id: str, img: Union[bool, None] = None):
if img:
return FileResponse("image.png", media_type="image/png")
else:
return {"id": "foo", "value": "there goes my hero"}
Note
注意,你必须直接使用 FileResponse
返回图像。
Info
除非你在 responses
参数中明确指定不同的媒体类型,否则 FastAPI 会假设响应与主响应类具有相同的媒体类型(默认 application/json
)。
但如果你指定了一个自定义响应类,其媒体类型为 None
,FastAPI 会为任何具有关联模型的附加响应使用 application/json
。
组合信息¶
你还可以从多个地方组合响应信息,包括 response_model
、status_code
和 responses
参数。
你可以声明一个 response_model
,使用默认的状态码 200
(或根据需要自定义),然后在 responses
中直接在 OpenAPI 模式中为相同的响应声明附加信息。
FastAPI 将保留来自 responses
的附加信息,并将其与你的模型的 JSON Schema 结合起来。
例如,你可以声明一个使用 Pydantic 模型并具有自定义 description
的状态码为 404
的响应。
以及一个使用你的 response_model
的状态码为 200
的响应,但包含自定义的 example
:
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from pydantic import BaseModel
class Item(BaseModel):
id: str
value: str
class Message(BaseModel):
message: str
app = FastAPI()
@app.get(
"/items/{item_id}",
response_model=Item,
responses={
404: {"model": Message, "description": "The item was not found"},
200: {
"description": "Item requested by ID",
"content": {
"application/json": {
"example": {"id": "bar", "value": "The bar tenders"}
}
},
},
},
)
async def read_item(item_id: str):
if item_id == "foo":
return {"id": "foo", "value": "there goes my hero"}
else:
return JSONResponse(status_code=404, content={"message": "Item not found"})
所有这些都将被组合并包含在你的 OpenAPI 中,并在 API 文档中显示:
组合预定义响应和自定义响应¶
你可能希望有一些适用于多个 路径操作 的预定义响应,但你也希望将它们与每个 路径操作 所需的自定义响应结合起来。
对于这些情况,你可以使用 Python 技术,通过 **dict_to_unpack
来“解包”一个 dict
:
old_dict = {
"old key": "old value",
"second old key": "second old value",
}
new_dict = {**old_dict, "new key": "new value"}
在这里,new_dict
将包含 old_dict
中的所有键值对以及新的键值对:
{
"old key": "old value",
"second old key": "second old value",
"new key": "new value",
}
你可以使用这种技术在你的 路径操作 中重用一些预定义的响应,并将它们与额外的自定义响应结合起来。
例如:
from typing import Union
from fastapi import FastAPI
from fastapi.responses import FileResponse
from pydantic import BaseModel
class Item(BaseModel):
id: str
value: str
responses = {
404: {"description": "Item not found"},
302: {"description": "The item was moved"},
403: {"description": "Not enough privileges"},
}
app = FastAPI()
@app.get(
"/items/{item_id}",
response_model=Item,
responses={**responses, 200: {"content": {"image/png": {}}}},
)
async def read_item(item_id: str, img: Union[bool, None] = None):
if img:
return FileResponse("image.png", media_type="image/png")
else:
return {"id": "foo", "value": "there goes my hero"}
关于 OpenAPI 响应的更多信息¶
要查看你可以在响应中包含的具体内容,你可以查看 OpenAPI 规范中的这些部分:
- OpenAPI Responses Object,它包括
Response Object
。 - OpenAPI Response Object,你可以直接在
responses
参数中的每个响应中包含此处的任何内容。包括description
、headers
、content
(在其中声明不同的媒体类型和 JSON Schemas)和links
。