Skip to content

[P1][openapi] 分离文档生成与端点挂载,明确中间件覆盖范围 #88

Closed

Description

@Kilerd

问题与影响

开启 openapi feature 后,into_axum_router 在用户已配置的路由及 layer 之后自动追加 /openapi.json、/redoc 和 /scalar。配置在 GotchaRouter 上的鉴权等中间件不会覆盖这些新端点。编译期功能开关同时决定了 HTTP 暴露策略,应用无法只生成 schema 或显式选择文档挂载策略。
此外 Gotcha::with_openapi() 只返回 self,是一个没有行为的公开方法。

已验证的复现

在本地源码上通过 GotchaApp::build_router 装配应用,为业务路由添加始终返回 401 的 middleware:

  • 请求业务路由返回 401。
  • 请求 /openapi.json 返回 200。
    这说明中间件覆盖范围与应用级配置直觉不一致;应用仍可通过在最终 Axum router 上额外加 layer 规避。

代码位置

调整方向

将文档生成、文档路由挂载和最终全应用 middleware 装配分开。提供显式的文档端点开关、路径及覆盖策略;明确区分业务路由 middleware 与最终全应用 middleware。处理 with_openapi() 的实际语义或弃用迁移。

验收标准

  • 能生成 schema 而不暴露文档 HTTP 端点(与 [openapi] The assembled spec is unreachable without an HTTP request #77 协同)。
  • 文档端点可以禁用或自定义挂载路径。
  • 显式应用于整个应用的鉴权覆盖业务接口和三个文档端点。
  • 支持业务受保护、文档公开这一显式配置,避免只能依赖调用顺序。
  • 测试覆盖两套 API 的装配行为及 with_openapi() 的最终语义。
  • 写明默认行为变化与迁移方式。

相关:#77、#81。

当前版本复查与已批准方案

基于 PR #114 合入后的 main 7cea0fef855cd0b16a3df2d101a1f26a8cc92d09 复查:

  • GotchaRouter::into_axum_router 仍在已有业务路由/layer 之后追加三个固定文档端点。
  • Gotcha::with_openapi() 仍只返回 self;Redoc/Scalar 模板的文档地址也固定为 /openapi.json。
  • openapi transform 为 FnOnce,因此无 HTTP 导出应采用消费构建对象的入口,保留现有 transform 顺序和单次执行约定;不应为导出而将回调改成 Clone/Fn。

用户已批准以下范围,已实现于 PR #115:

  1. 编译 openapi feature 只提供文档能力,默认不挂载文档 HTTP 端点。with_openapi() 显式启用默认的 /openapi.json、/redoc、/scalar;应用级端点配置支持关闭和自定义路径,两个 UI 使用实际配置的 JSON 地址。
  2. 文档端点配置属于顶层应用(Gotcha builder / GotchaApp 对应入口),不随业务子路由 nest/merge 隐式传播。
  3. 保留现有 layer 对已注册业务路由的 Axum 语义,新增 builder app_layer 及 trait 对应的最终装配 hook。全应用 middleware 在业务路由和文档端点组装完成后统一应用,覆盖范围不依赖 with_openapi 的调用顺序。仅在业务路由上加 layer 时,文档可以显式保持公开。
  4. 提供消费路由构建对象的文档导出入口,直接取得经过全部 transform 的 OpenAPI 文档,不初始化应用状态、绑定端口或启动后台任务;与 HTTP 文档复用同一生成流程,协同解决 [openapi] The assembled spec is unreachable without an HTTP request #77。
  5. 通过两套 API 的必要集成测试验证默认禁用、显式启用、自定义文档地址、全应用鉴权和业务鉴权的覆盖范围;验证导出与 HTTP 文档等价,并沿用既有 transform 顺序测试。

默认行为的迁移必须明确:此前仅打开 feature 就暴露文档的应用,需要显式启用端点;已有 with_openapi() 调用获得实际行为。现有业务 layer 不静默改变范围,全应用覆盖通过新入口明确选择。

本次提案只覆盖文档生成/挂载和 middleware 装配边界,#81 的其他路由能力继续单独跟踪。PR #115 已由用户于 2026-09-14 合入,merge commit 90e0efeff99ebcd032f054193cf632bafb5e5a95。

实现与验证

  • PR refactor(openapi)!: make documentation endpoints explicit #115:默认关闭文档 HTTP 端点;with_openapi() 显式启用默认路径。OpenApiEndpoints 配置 JSON、Redoc、Scalar 路径及 UI 关闭,两个 UI 使用配置后的 JSON URL。
  • 共享 assembly::assemble 依次装配业务路由、可选文档、全应用 middleware。Builder app_layer 和 trait finish_router 覆盖全部端点及 fallback,原有 layer 保留已注册业务路由范围。
  • Gotcha::into_openapi()、GotchaRouter::into_openapi()、GotchaApp::openapi_document() 使用同一生成/transform 流程直接导出文档;无需配置加载、状态初始化、端口或任务,协同解决 [openapi] The assembled spec is unreachable without an HTTP request #77。未启用文档且未请求导出时不生成文档、不执行 transform。
  • 7 项装配测试覆盖两套 API、开关/自定义地址/单独关闭 UI、HTML 地址转义、鉴权范围、全应用 layer 顺序与 fallback、配置错误提前返回、导出与 HTTP 字节等价和回调次数、无 runtime 导出。1 项 unit test 集中覆盖端点路径验证。
  • 原有文档集成测试和 OpenAPI/Extension 示例已显式开启端点,README/API 文档/MIGRATION 完成迁移说明。移除不再使用的 gotcha 直接 cfg-if 依赖。
  • 本地 gotcha 默认/all-features 测试(含 doctest)、workspace all-features check、默认 check、workspace Clippy、格式检查及拒绝 warning 的文档构建全部通过。
  • 实际执行 OpenAPI 示例 --export-openapi,工作目录包含无效配置文件仍成功输出 7 条路径、5 个 component schema;不创建运行时或启动应用。
  • GitHub CI:commit 4b25e914e3abba446bd6dcd77898e9c757570c5c 的 11 项检查全部通过,见 run 34817382358。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions