跳到内容

OpenAPI 中的附加响应

警告

这是一个相当高级的主题。

如果您刚开始使用 FastAPI,您可能不需要这个。

您可以声明附加响应,带有附加状态码、媒体类型、描述等。

这些附加响应将被包含在 OpenAPI 架构中,因此它们也会出现在 API 文档中。

但是对于这些附加响应,您必须确保直接返回一个 Response,例如 JSONResponse,并带上您的状态码和内容。

model 的附加响应

您可以为您的路由操作装饰器传递一个 responses 参数。

它接收一个 dict:键是每个响应的状态码(例如 200),值是包含每个响应信息的其他 dict

这些响应 dict 中的每一个都可以有一个 model 键,其中包含一个 Pydantic 模型,就像 response_model 一样。

FastAPI 将采用该模型,生成其 JSON Schema,并将其包含在 OpenAPI 的正确位置。

例如,要使用 Pydantic 模型 Message 声明另一个状态码为 404 的响应,您可以这样写

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 会在此处引用 OpenAPI 中另一个位置的全局 JSON Schema,而不是直接包含它。这样,其他应用程序和客户端就可以直接使用这些 JSON Schema,提供更好的代码生成工具等。

为此路由操作生成的 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"
                    }
                }
            }
        }
    }
}

Schema 被引用到 OpenAPI schema 的另一个位置

{
    "components": {
        "schemas": {
            "Message": {
                "title": "Message",
                "required": [
                    "message"
                ],
                "type": "object",
                "properties": {
                    "message": {
                        "title": "Message",
                        "type": "string"
                    }
                }
            },
            "Item": {
                "title": "Item",
                "required": [
                    "id",
                    "value"
                ],
                "type": "object",
                "properties": {
                    "id": {
                        "title": "Id",
                        "type": "string"
                    },
                    "value": {
                        "title": "Value",
                        "type": "string"
                    }
                }
            },
            "ValidationError": {
                "title": "ValidationError",
                "required": [
                    "loc",
                    "msg",
                    "type"
                ],
                "type": "object",
                "properties": {
                    "loc": {
                        "title": "Location",
                        "type": "array",
                        "items": {
                            "type": "string"
                        }
                    },
                    "msg": {
                        "title": "Message",
                        "type": "string"
                    },
                    "type": {
                        "title": "Error Type",
                        "type": "string"
                    }
                }
            },
            "HTTPValidationError": {
                "title": "HTTPValidationError",
                "type": "object",
                "properties": {
                    "detail": {
                        "title": "Detail",
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/ValidationError"
                        }
                    }
                }
            }
        }
    }
}

主响应的其他媒体类型

您可以使用相同的 responses 参数为同一个主响应添加不同的媒体类型。

例如,您可以添加一个额外的媒体类型 image/png,声明您的路由操作可以返回一个 JSON 对象(媒体类型为 application/json)或一个 PNG 图像

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: bool | None = None):
    if img:
        return FileResponse("image.png", media_type="image/png")
    else:
        return {"id": "foo", "value": "there goes my hero"}
🤓 其他版本和变体
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"}

注意

请注意,您必须直接使用 FileResponse 返回图像。

信息

除非您在 responses 参数中明确指定了不同的媒体类型,否则 FastAPI 将假定响应的媒体类型与主响应类相同(默认为 application/json)。

但是,如果您指定了一个自定义的响应类,其媒体类型为 None,FastAPI 将为任何具有关联模型的附加响应使用 application/json

信息合并

您还可以合并来自多个地方的响应信息,包括 response_modelstatus_coderesponses 参数。

您可以声明一个 response_model,使用默认状态码 200(或者一个自定义的,如果需要的话),然后在 responses 中为同一个响应声明附加信息,直接在 OpenAPI schema 中。

FastAPI 将保留 responses 中的附加信息,并将其与您模型的 JSON Schema 合并。

例如,您可以声明一个状态码为 404 的响应,该响应使用 Pydantic 模型并具有自定义的 description

以及一个状态码为 200 的响应,该响应使用您的 response_model,但包含一个自定义的 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

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 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: bool | None = None):
    if img:
        return FileResponse("image.png", media_type="image/png")
    else:
        return {"id": "foo", "value": "there goes my hero"}
🤓 其他版本和变体
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 对象,它包含了 Response Object
  • OpenAPI Response 对象,您可以直接将此中的任何内容包含在您 responses 参数中的每个响应中。包括 descriptionheaderscontent(在此其中您可以声明不同的媒体类型和 JSON Schemas),以及 links