Skip to content

OpenAPI 中的額外回應

🌐 AI 與人類共同完成的翻譯

此翻譯由人類指導的 AI 完成。🤝

可能會有對原意的誤解,或讀起來不自然等問題。🤖

你可以透過協助我們更好地引導 AI LLM來改進此翻譯。

英文版

警告

這是一個偏進階的主題。

如果你剛開始使用 FastAPI,大多情況下不需要用到它。

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

這些額外回應會被包含在 OpenAPI 中,因此也會顯示在 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 中以參照的方式指向其他位置:

{
    "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"}

注意

請注意你必須直接用 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 技巧,透過 **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"}

關於 OpenAPI 回應的更多資訊

若要查看回應中究竟可以包含哪些內容,你可以參考 OpenAPI 規範中的這些章節:

  • OpenAPI Responses 物件,其中包含 Response Object
  • OpenAPI Response 物件,你可以把這裡的任何內容直接放到 responses 參數內各個回應中。包含 descriptionheaderscontent(在其中宣告不同的媒體型別與 JSON Schemas)、以及 links