跳至內容

進階路徑操作設定

OpenAPI operationId

警告

如果您不是 OpenAPI 的「專家」,您可能不需要這個。

您可以使用參數 operation_id 設定 OpenAPI 的 operationId,以用於您的*路徑操作*。

您必須確保每個操作的 operationId 都是唯一的。

from fastapi import FastAPI

app = FastAPI()


@app.get("/items/", operation_id="some_specific_id_you_define")
async def read_items():
    return [{"item_id": "Foo"}]

使用*路徑操作函式*名稱作為 operationId

如果您想使用 API 的函式名稱作為 operationId,您可以逐一查看所有函式,並使用它們的 APIRoute.name 覆寫每個*路徑操作*的 operation_id

您應該在新增所有*路徑操作*之後執行此操作。

from fastapi import FastAPI
from fastapi.routing import APIRoute

app = FastAPI()


@app.get("/items/")
async def read_items():
    return [{"item_id": "Foo"}]


def use_route_names_as_operation_ids(app: FastAPI) -> None:
    """
    Simplify operation IDs so that generated API clients have simpler function
    names.

    Should be called only after all routes have been added.
    """
    for route in app.routes:
        if isinstance(route, APIRoute):
            route.operation_id = route.name  # in this case, 'read_items'


use_route_names_as_operation_ids(app)

提示

如果您手動呼叫 app.openapi(),您應該在此之前更新 operationId

警告

如果您這樣做,您必須確保每個*路徑操作函式*都有一個唯一的名稱。

即使它們位於不同的模組(Python 檔案)中。

從 OpenAPI 中排除

要從產生的 OpenAPI 結構描述(以及自動文件系統)中排除*路徑操作*,請使用參數 include_in_schema 並將其設定為 False

from fastapi import FastAPI

app = FastAPI()


@app.get("/items/", include_in_schema=False)
async def read_items():
    return [{"item_id": "Foo"}]

從 docstring 取得進階描述

您可以限制 OpenAPI 使用*路徑操作函式*的 docstring 的行數。

新增 \f(一個跳脫的「換頁」字元)會導致 FastAPI 在此處截斷用於 OpenAPI 的輸出。

它不會顯示在文件中,但其他工具(例如 Sphinx)將能夠使用其餘部分。

from typing import Set, Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None
    tags: Set[str] = set()


@app.post("/items/", response_model=Item, summary="Create an item")
async def create_item(item: Item):
    """
    Create an item with all the information:

    - **name**: each item must have a name
    - **description**: a long description
    - **price**: required
    - **tax**: if the item doesn't have tax, you can omit this
    - **tags**: a set of unique tag strings for this item
    \f
    :param item: User input.
    """
    return item

額外回應

您可能已經看過如何為*路徑操作*宣告 response_modelstatus_code

這定義了關於*路徑操作*主要回應的元數據。

您也可以使用模型、狀態碼等宣告額外的回應。

文件中有一整個章節關於這個主題,您可以在 OpenAPI 中的額外回應 閱讀。

OpenAPI 額外資訊

當您在應用程式中宣告*路徑操作*時,FastAPI 會自動產生關於該*路徑操作*的相關元數據,以包含在 OpenAPI 結構描述中。

「技術細節」

在 OpenAPI 規範中,它被稱為 操作物件 (Operation Object)

它包含關於*路徑操作*的所有資訊,並用於產生自動文件。

它包含 tagsparametersrequestBodyresponses 等等。

這個*路徑操作*特定的 OpenAPI 結構描述通常由 FastAPI 自動產生,但您也可以擴充它。

提示

這是一個低階的擴充點。

如果您只需要宣告額外的回應,更方便的做法是使用OpenAPI 中的額外回應

您可以使用參數 openapi_extra 擴展 *路徑操作* 的 OpenAPI 模式。

OpenAPI 擴展

這個 openapi_extra 很有幫助,例如,用於宣告 OpenAPI 擴展

from fastapi import FastAPI

app = FastAPI()


@app.get("/items/", openapi_extra={"x-aperture-labs-portal": "blue"})
async def read_items():
    return [{"item_id": "portal-gun"}]

如果您打開自動 API 文件,您的擴展將顯示在特定 *路徑操作* 的底部。

如果您查看生成的 OpenAPI(在 API 中的 /openapi.json 處),您也會看到您的擴展作為特定 *路徑操作* 的一部分。

{
    "openapi": "3.1.0",
    "info": {
        "title": "FastAPI",
        "version": "0.1.0"
    },
    "paths": {
        "/items/": {
            "get": {
                "summary": "Read Items",
                "operationId": "read_items_items__get",
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {}
                            }
                        }
                    }
                },
                "x-aperture-labs-portal": "blue"
            }
        }
    }
}

自定義 OpenAPI *路徑操作* 模式

openapi_extra 中的字典將與自動生成的 *路徑操作* 的 OpenAPI 模式深度合併。

因此,您可以將額外資料添加到自動生成的模式中。

例如,您可以決定使用自己的程式碼讀取和驗證請求,而不使用 FastAPI 和 Pydantic 的自動功能,但您仍然可能希望在 OpenAPI 模式中定義請求。

您可以使用 openapi_extra 來做到這一點。

from fastapi import FastAPI, Request

app = FastAPI()


def magic_data_reader(raw_body: bytes):
    return {
        "size": len(raw_body),
        "content": {
            "name": "Maaaagic",
            "price": 42,
            "description": "Just kiddin', no magic here. ✨",
        },
    }


@app.post(
    "/items/",
    openapi_extra={
        "requestBody": {
            "content": {
                "application/json": {
                    "schema": {
                        "required": ["name", "price"],
                        "type": "object",
                        "properties": {
                            "name": {"type": "string"},
                            "price": {"type": "number"},
                            "description": {"type": "string"},
                        },
                    }
                }
            },
            "required": True,
        },
    },
)
async def create_item(request: Request):
    raw_body = await request.body()
    data = magic_data_reader(raw_body)
    return data

在此範例中,我們沒有宣告任何 Pydantic 模型。事實上,請求主體甚至沒有被解析為 JSON,而是直接作為 bytes 讀取,並且函數 magic_data_reader() 將負責以某種方式解析它。

儘管如此,我們仍然可以在 OpenAPI 模式中宣告預期的請求主體模式。

自定義 OpenAPI 內容類型

使用相同的技巧,您可以使用 Pydantic 模型來定義 JSON 模式,然後將其包含在 *路徑操作* 的自定義 OpenAPI 模式區段中。

即使請求中的資料類型不是 JSON,您也可以這樣做。

例如,在此應用程式中,我們沒有使用 FastAPI 的整合功能從 Pydantic 模型中提取 JSON 模式,也沒有使用 JSON 的自動驗證。事實上,我們將請求內容類型宣告為 YAML,而不是 JSON。

from typing import List

import yaml
from fastapi import FastAPI, HTTPException, Request
from pydantic import BaseModel, ValidationError

app = FastAPI()


class Item(BaseModel):
    name: str
    tags: List[str]


@app.post(
    "/items/",
    openapi_extra={
        "requestBody": {
            "content": {"application/x-yaml": {"schema": Item.model_json_schema()}},
            "required": True,
        },
    },
)
async def create_item(request: Request):
    raw_body = await request.body()
    try:
        data = yaml.safe_load(raw_body)
    except yaml.YAMLError:
        raise HTTPException(status_code=422, detail="Invalid YAML")
    try:
        item = Item.model_validate(data)
    except ValidationError as e:
        raise HTTPException(status_code=422, detail=e.errors(include_url=False))
    return item
from typing import List

import yaml
from fastapi import FastAPI, HTTPException, Request
from pydantic import BaseModel, ValidationError

app = FastAPI()


class Item(BaseModel):
    name: str
    tags: List[str]


@app.post(
    "/items/",
    openapi_extra={
        "requestBody": {
            "content": {"application/x-yaml": {"schema": Item.schema()}},
            "required": True,
        },
    },
)
async def create_item(request: Request):
    raw_body = await request.body()
    try:
        data = yaml.safe_load(raw_body)
    except yaml.YAMLError:
        raise HTTPException(status_code=422, detail="Invalid YAML")
    try:
        item = Item.parse_obj(data)
    except ValidationError as e:
        raise HTTPException(status_code=422, detail=e.errors())
    return item

資訊

在 Pydantic 版本 1 中,獲取模型 JSON 模式的 方法稱為 Item.schema(),在 Pydantic 版本 2 中,該方法稱為 Item.model_json_schema()

儘管如此,雖然我們沒有使用預設的整合功能,但我們仍然使用 Pydantic 模型手動生成我們希望以 YAML 接收的資料的 JSON 模式。

然後我們直接使用請求,並將主體提取為 bytes。這表示 FastAPI 甚至不會嘗試將請求有效負載解析為 JSON。

然後在我們的程式碼中,我們直接解析 YAML 內容,然後再次使用相同的 Pydantic 模型來驗證 YAML 內容。

from typing import List

import yaml
from fastapi import FastAPI, HTTPException, Request
from pydantic import BaseModel, ValidationError

app = FastAPI()


class Item(BaseModel):
    name: str
    tags: List[str]


@app.post(
    "/items/",
    openapi_extra={
        "requestBody": {
            "content": {"application/x-yaml": {"schema": Item.model_json_schema()}},
            "required": True,
        },
    },
)
async def create_item(request: Request):
    raw_body = await request.body()
    try:
        data = yaml.safe_load(raw_body)
    except yaml.YAMLError:
        raise HTTPException(status_code=422, detail="Invalid YAML")
    try:
        item = Item.model_validate(data)
    except ValidationError as e:
        raise HTTPException(status_code=422, detail=e.errors(include_url=False))
    return item
from typing import List

import yaml
from fastapi import FastAPI, HTTPException, Request
from pydantic import BaseModel, ValidationError

app = FastAPI()


class Item(BaseModel):
    name: str
    tags: List[str]


@app.post(
    "/items/",
    openapi_extra={
        "requestBody": {
            "content": {"application/x-yaml": {"schema": Item.schema()}},
            "required": True,
        },
    },
)
async def create_item(request: Request):
    raw_body = await request.body()
    try:
        data = yaml.safe_load(raw_body)
    except yaml.YAMLError:
        raise HTTPException(status_code=422, detail="Invalid YAML")
    try:
        item = Item.parse_obj(data)
    except ValidationError as e:
        raise HTTPException(status_code=422, detail=e.errors())
    return item

資訊

在 Pydantic 版本 1 中,解析和驗證物件的方法稱為 Item.parse_obj(),在 Pydantic 版本 2 中,該方法稱為 Item.model_validate()

提示

這裡我們重複使用相同的 Pydantic 模型。

但同樣地,我們也可以用其他方式驗證它。