首页
/ FastAPI path operation 高级配置:operationId、include_in_schema、docstring 截断与 openapi_extra 全解析

FastAPI path operation 高级配置:operationId、include_in_schema、docstring 截断与 openapi_extra 全解析

2026-09-07 16:07:25作者:柏廷章Berta

本篇基于 FastAPI 官方文档《Path Operation の高度な設定》(日文版)展开,系统讲解 path operation 级别的 OpenAPI 高级配置:如何自定义 operation_id、如何用 include_in_schema 将路由从自动文档中隐藏、如何用 \f 字符控制 docstring 进入 OpenAPI 的描述长度,以及如何通过 openapi_extra 扩展甚至重写 OpenAPI 的 Operation Object。读完本文,你将掌握在不改动 FastAPI 自动文档机制的前提下,对每个路由的 OpenAPI 元数据做精确控制的完整手段,并能结合仓库源码理解每个参数的底层实现位置。

一、自定义 OpenAPI operationId

1. 通过 operation_id 参数指定

每个 path operation(即路由函数)在生成的 OpenAPI 文档中都会有一个 operationId。如果默认的命名规则不符合你的需要(例如你在为 API 生成客户端代码时希望使用稳定的、自定义的标识符),可以直接在路由装饰器中传入 operation_id 参数:

from fastapi import FastAPI

app = FastAPI()


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

完整示例见 tutorial001_py310.py

注意事项(官方文档明确警告):

  • 除非你是 OpenAPI 的"专家",否则通常不需要手动指定 operationId
  • 手动指定的 operation_id 必须在整个 API 中唯一,多个路由不能重复使用同一个值。

2. 使用函数名作为 operationId:generate_unique_id_function

如果你希望统一地用 path operation 函数名 作为 operationId(而不是默认的"函数名+路径+方法"组合),可以给 FastAPI 传入一个自定义的 generate_unique_id_function

from fastapi import FastAPI
from fastapi.routing import APIRoute


def custom_generate_unique_id(route: APIRoute) -> str:
    return route.name


app = FastAPI(generate_unique_id_function=custom_generate_unique_id)


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

完整示例见 tutorial002_py310.py

这个自定义函数的签名是接收每个 APIRoute 并返回字符串形式的 operationId。官方文档警告:采用这种方式时,每个 path operation 函数 必须具有唯一的名字,即使它们分布在不同模块(不同 Python 文件)中——因为函数名 route.name 本身并不包含模块路径,不同文件里同名的函数会产生冲突。

默认算法的源码佐证:仓库中 FastAPI 内置的 generate_unique_id 位于 fastapi/utils.py,其实现为:

def generate_unique_id(route: "APIRoute") -> str:
    operation_id = f"{route.name}{route.path_format}"
    operation_id = re.sub(r"\W", "_", operation_id)
    assert route.methods
    operation_id = f"{operation_id}_{list(route.methods)[0].lower()}"
    return operation_id

即"函数名 + 路径格式"拼接后,把所有非单词字符替换为下划线,再追加小写化的首个 HTTP 方法。这也解释了为什么文档示例中 /items/ 的 GET 路由生成了 read_items_items__get 这样的 operationId(read_items + /items/ 转下划线 + _get)。该函数在 fastapi/openapi/utils.py 中被导入用于构建 Operation Object,而路由层对它的覆盖逻辑(DefaultPlaceholder 继承自 Router 的机制)集中在 fastapi/routing.py 中——从源码结构看,generate_unique_id_function 支持在 FastAPIAPIRouter 以及 include_router 三个层级配置并逐层继承。

二、从 OpenAPI 中排除路由:include_in_schema=False

有时某个 path operation 仍然需要正常工作,但不希望出现在生成的 OpenAPI Schema(也就是自动文档)中。只需将 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"}]

完整示例见 tutorial003_py310.py。设置后,该路由的接口依然可以正常访问,但不会出现在 Swagger UI / ReDoc 文档以及 /openapi.json 中。这对内部接口、健康检查端点或尚在调试中的路由非常实用。

三、docstring 描述的精确控制:\f 截断

path operation 函数 的 docstring 会被 FastAPI 用作 OpenAPI 中该操作的描述文本。当 docstring 较长时,可以用转义的换页符(form feed)\f 来划定边界——FastAPI 只取 \f 之前的部分作为 OpenAPI 描述,\f 之后的内容不进入文档,但仍可被 Sphinx 等其他文档工具使用:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


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


@app.post("/items/", summary="Create an item")
async def create_item(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

完整示例见 tutorial004_py310.py。注意 \f 位于列表之后、:param item: 之前:列表部分会进入 OpenAPI 描述,而 Sphinx 风格的参数说明则被截断在文档之外。

源码级实现:截断逻辑就在路由注册阶段完成。fastapi/routing.py 中有如下代码:

# if a "form feed" character (page break) is found in the description text,
# truncate description text to the content preceding the first "form feed"
route.description = route.description.split("\f")[0].strip()

即:找到第一个换页符,取之前的内容并 strip()。类似的截断还出现在 Pydantic v2 兼容层中,fastapi/_compat/v2.py 对字段描述也做了同样的 split("\f")[0] 处理,说明这一约定贯穿了 OpenAPI 描述生成的多个环节。

四、附加响应(additional responses)

你已经见过在 path operation 上声明 response_modelstatus_code 的方式,它定义了该操作主响应的元数据。除此之外,还可以声明更多附加响应(各自的模型、状态码等)。官方文档将其单独成章,详见 OpenAPI 的附加响应(日文版),对应的仓库示例代码位于 docs_src/additional_responses/ 目录。

五、openapi_extra:扩展路径操作的 OpenAPI Schema

5.1 Operation Object 与低级别扩展点

当你在应用中声明 path operation 时,FastAPI 会自动生成与其关联的元数据并放入 OpenAPI Schema,这就是 OpenAPI 规范中的 Operation Object,包含了该操作的全部信息(tagsparametersrequestBodyresponses 等),也是自动文档生成的直接依据。

openapi_extra 参数允许你向这个自动生成的 Schema 中注入额外数据。官方文档将其定位为低级别的扩展点:如果只是要添加额外响应,更推荐上文提到的附加响应机制;只有需要直接操作 Operation Object 时才使用 openapi_extra

5.2 声明 OpenAPI Extensions

openapi_extra 最直接用途是声明以 x- 开头的 OpenAPI 规范扩展(Specification Extensions):

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

完整示例见 tutorial005_py310.py

Swagger UI 中显示该路径操作自定义扩展 x-aperture-labs-portal: blue

打开自动 API 文档后,这个扩展会显示在该 path operation 的下方;而在 /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"
            }
        }
    }
}

5.3 自定义 OpenAPI path operation Schema:不依赖 Pydantic 也定义 requestBody

openapi_extra 内的字典会与自动生成的 OpenAPI Schema 进行深度合并(deep merge),因此你可以向 Schema 中追加原本 FastAPI 不会生成的字段。

典型场景:你选择不用 Pydantic 的自动功能,而是自己读取并校验请求,但仍希望在 OpenAPI 中声明请求体的结构。此时可以让端点直接接收 Request,把原始请求体作为 bytes 读取,同时用 openapi_extra 手动写 requestBody

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

完整示例见 tutorial006_py310.py。这个例子没有声明任何 Pydantic 模型,请求体也不会被解析为 JSON,而是直接以 bytes 读取,交给 magic_data_reader() 自行处理;但 OpenAPI 文档中依然展示了一个完整、正确的 JSON Schema 请求体定义。

5.4 自定义 OpenAPI content type:YAML 请求体

同样的技巧还能处理非 JSON 请求内容类型。下面的示例声明请求体的 content type 为 application/x-yaml,Schema 来自 Pydantic 模型 Item 手动生成的 JSON Schema,但完全不使用 FastAPI 的 JSON 自动解析/校验功能:

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

完整示例见 tutorial007_py310.py

该示例的工作流程值得注意:

  1. Schema 生成Item.model_json_schema() 从 Pydantic 模型静态抽取 JSON Schema,注入 openapi_extraapplication/x-yaml content 段——文档层面正确描述了你期望的 YAML 数据形状;
  2. 原始读取await request.body() 拿到 bytes,FastAPI 甚至不会尝试把它当 JSON 解析;
  3. 手动解析与校验yaml.safe_load 解析 YAML(解析失败抛出 422),再用同一个 Item 模型 model_validate 做数据校验(校验失败同样返回 422,且通过 include_url=False 精简错误输出)。

官方文档提示:这里复用了同一个 Pydantic 模型,但你同样可以用其他方式做解析与校验——openapi_extra 只关心"文档里声明什么",与"运行时代码怎么处理"完全解耦。

5.5 合并机制的源码佐证

openapi_extra 参数在 fastapi/routing.py 中从 APIRoute 定义开始,贯穿 APIRouterinclude_router 以及所有 get/post/put/patch/delete... 装饰器透传(该文件中有十余处同名参数签名),最终在 OpenAPI 构建阶段与自动生成的 Operation Object 深度合并。合并所用的 deep_dict_update 工具函数定义在 fastapi/utils.py 中,并被 fastapi/openapi/utils.py 导入用于 OpenAPI 文档生成流程——从源码结构看,openapi_extra 的每个键都会递归覆盖/追加到对应路径的操作 Schema 中,这保证了你在扩展 requestBody 时可以只写差异部分而不必重写整个 Operation Object。

小结

参数 作用层级 适用场景
operation_id 单个路由 为指定操作设置唯一的自定义 OpenAPI operationId
generate_unique_id_function 应用 / 路由器 统一改变所有路由的 operationId 生成规则(如直接用函数名)
include_in_schema 单个路由 保留接口功能但将其从自动文档中隐藏
docstring 中的 \f 单个路由的 docstring 限制进入 OpenAPI 的描述长度,其余留给 Sphinx 等工具
openapi_extra 单个路由 低级别扩展 Operation Object:x- 扩展字段、自定义 requestBody 与非 JSON content type

以上配置均只影响 OpenAPI 元数据的生成,不改变路由本身的实际行为(include_in_schema 影响文档、openapi_extra 只影响 Schema 声明),是 FastAPI 提供的一组"文档侧"精细控制旋钮。所有示例代码均可在 docs_src/path_operation_advanced_configuration/ 目录下运行验证,对应的英文对照文档位于 docs/en/docs/advanced/path-operation-advanced-configuration.md

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391