跳至內容

OpenAPI 中的額外回應

警告

這是一個相當進階的主題。

如果您剛開始使用 FastAPI,您可能不需要用到這個。

您可以宣告額外的回應,包含額外的狀態碼、媒體類型、描述等等。

這些額外的回應將會包含在 OpenAPI schema 中,所以它們也會顯示在 API 文件中。

但對於這些額外的回應,您必須確保直接返回一個像 JSONResponse 之類的 Response,並附帶您的狀態碼和內容。

使用 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 在這裡新增一個參考,指向 OpenAPI 中其他位置的全局 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"
                    }
                }
            }
        }
    }
}

這些 schemas 被參考到 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 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 參數。

您可以使用預設狀態碼 200(或您需要的自定義狀態碼)宣告 response_model,然後在 OpenAPI schema 中的 responses 中直接為該回應宣告額外資訊。

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 來解包一個 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 回應物件,它包含了 回應物件
  • OpenAPI 回應物件,您可以直接將其中的任何內容包含在 responses 參數內的每個回應中。包括 description(描述)、headers(標頭)、content(內容)(在其中宣告不同的媒體類型和 JSON Schema)以及 links(連結)。