grpc-gateway 怎么把多个 proto 文件的 OpenAPI 输出合并为单个 swagger 文件?
2026/9/14 17:56:15 网站建设 项目流程

grpc-gateway 怎么把多个 proto 文件的 OpenAPI 输出合并为单个 swagger 文件?

【免费下载链接】grpc-gatewaygRPC to JSON proxy generator following the gRPC HTTP spec项目地址: https://gitcode.com/GitHub_Trending/gr/grpc-gateway

如果你的 protobuf 定义分散在多个.proto文件里,grpc-gateway 的 OpenAPI 插件默认会为每个输入文件各生成一个输出文件:protoc-gen-openapiv2为每个.proto生成一个*.swagger.jsonprotoc-gen-openapiv3为每个.proto生成一个*.openapi.json。问题在于,OpenAPI 的规范本身把一次 API 描述为一个文档,把规格拆散到多个文件会改变 schema 结构,也直接喂给很多 OpenAPI 消费者(UI 查看器、客户端生成器、网关配置)。

针对这个"多个 proto → 单个 OpenAPI 文档"的任务,仓库里有两条官方路径:

  • 使用protoc-gen-openapiv2(输出 Swagger 2.0 / OpenAPI 2.x,生成foo.swagger.json):给插件加allow_mergemerge_file_name两个选项,在一次生成中直接输出单个合并文件。
  • 使用protoc-gen-openapiv3(输出 OpenAPI 3.1.0 JSON):先生成每个文件一份的输出,再用配套的openapiv3-merge工具把多份 JSON 合并成一个文档。

两条路径都只作用于生成的 OpenAPI 规格文件,不改变 gateway 运行时的行为。下面分别给出完整操作步骤。

路径一:protoc-gen-openapiv2 用 allow_merge 直接合并

这是生成单个swagger.json的最短路径,合并发生在插件内部,不需要后处理脚本。

方式 A:protoc 命令行

protoc调用上追加--openapiv2_opt allow_merge=true,merge_file_name=foo

protoc -I. \ --openapiv2_out=. \ --openapiv2_opt allow_merge=true,merge_file_name=foo \ path/to/a.proto path/to/b.proto

merge_file_name是合并后目标文件的文件名前缀,foo会产出单个foo.swagger.json,而不是按 proto 文件数量散落的多个*.swagger.json。两个选项在 protoc-gen-openapiv2/main.go 中的定义也确认了这一点:allow_merge为"if set, generation one OpenAPI file out of multiple protos",merge_file_name为"target OpenAPI file name prefix after merge"。

方式 B:buf 配置

如果生成流程走buf.gen.yaml,把选项放进 plugin 的opt

- name: openapiv2 out: foo opt: allow_merge=true,merge_file_name=foo

文档特别提示:合并文件较多时,可能需要把 generation strategy 设为all

- name: openapiv2 out: foo strategy: all opt: allow_merge=true,merge_file_name=foo

合并后路径顺序

默认情况下生成的 Swagger 文件中 paths 按字母序排列。如果你希望合并后路径顺序跟随 proto 文件的书写顺序,加上preserve_rpc_order=true选项即可。文档明确说明该选项覆盖合并场景:"When merging protobuf files, paths will preserve their ordering depending on the order of files specified on the command line."

protoc --openapiv2_out=. --openapiv2_opt=preserve_rpc_order=true ./path/to/file.proto

或 buf:

version: v1 plugins: - name: openapiv2 out: . opt: - preserve_rpc_order=true

路径二:protoc-gen-openapiv3 用 openapiv3-merge 后处理合并

如果你需要的是 OpenAPI 3.1 输出,protoc-gen-openapiv3刻意遵循"1 个输入文件 → 1 个输出文件"的 protobuf 惯例,因此合并是独立的第二步:安装并运行 openapiv3-merge 工具。

注意protoc-gen-openapiv3目前是 Alpha 状态,输出 JSON 形状可能在 minor 版本之间变化;文档同时说明它不消费grpc.gateway.protoc_gen_openapiv2.options注解集,不产出 Swagger 2.0/YAML。如果你依赖 v2 注解或需要生产稳定的 OpenAPI 管线,应留在路径一。

安装

go install github.com/grpc-ecosystem/grpc-gateway/v2/openapiv3-merge@latest

生成每文件的 OpenAPI 文档

以 protoc 为例(buf 则是照常buf generate,在每个声明了 HTTP 绑定的 proto 旁出现一份.openapi.json):

#!/usr/bin/env bash set -euo pipefail protoc -I. \ --openapiv3_out=./gen \ $(find . -name '*.proto') root=gen/api/v1/api.openapi.json mapfile -t rest < <(find gen -name '*.openapi.json' ! -path "$root" | sort) openapiv3-merge "$root" "${rest[@]}" > gen/api.openapi.json

脚本里root指向上面这条命令会生成哪份文档,请按你的 proto 目录结构替换为实际路径;find收集的其余*.openapi.json按字典序排列,保证结果确定。

输入顺序很重要:合并文档的infoserversexternalDocs取自第一个输入,后面输入的这些字段会被丢弃。所以要把范围最宽、承载共享openapiv3_document注解(title/version/servers 等)的那份文件放最前。如果没有任何文件被指定为"根",文档的建议是全部按字典序排序、接受排序靠前的那份——但要在对应 proto 上设置openapiv3_document注解,合并结果的titleversionservers才有合理的值。

对于任意输入都能排第一的简单目录,也有 one-shot 写法:

openapiv3-merge $(find . -name '*.openapi.json' | sort) > api.openapi.json

结果验证

  • 合并文档写到 stdout(示例中重定向为gen/api.openapi.json),错误写到 stderr,进程以非零状态退出。
  • 产出的 spec 可验证为 OpenAPI 3.1.0,能直接喂给任意 3.1 兼容工具(文档给出的示例是openapi-generator-cli做客户端生成)。
  • 输出字段顺序遵循 OpenAPI 3.1.0 声明顺序(openapiinfoserverspathswebhookscomponentssecuritytagsexternalDocs,再跟扩展字段),paths/webhooks按输入顺序输出,components/*子图按 key 排序。

openapiv3-merge 的合并规则与冲突排查

合并器是严格模式:任何可能被静默覆盖的地方都会报错。字段级规则如下(来自 docs/docs/mapping/openapi_v3_merge.md):

字段规则
openapi所有输入必须一致
infoserversexternalDocs取第一个输入,后续输入的值被丢弃
pathswebhooks按输入顺序并集;同一路径内容不一致 → 报错
components/*并集,key 排序输出;同名内容不一致 → 报错
tagsname去重;同名但元数据不一致 → 报错
security第一个声明非空数组的输入胜出;后续声明不同非空数组 → 报错
未知顶层键(x-*扩展)取第一个输入

"内容不一致"是按规范化比较判定的:仅 key 顺序不同的两个值视为相等。由于protoc-gen-openapiv3用完全限定 proto 名(如example.v1.User)命名 component schema,跨包复用同一 message 时每个输出贡献的是同一个、内容一致的 component,合并可以干净通过。

合并失败时错误信息会指出冲突字段,文档给出的示例:

openapiv3-merge: paths."/v1/echo": echo.openapi.json redefines an entry with a different value

文档列出的常见原因:

  • 路径定义冲突:两个 proto 声明了相同 HTTP 路径但绑定不同,需要确定哪份是权威定义。
  • component 定义冲突:同名同包的全限定 message 名但形状不同——通常意味着 proto 树里存在重复的package+ message 声明。
  • tag 元数据冲突:两份openapiv3_document.tags注解对同名 tag 给出了不同描述,需统一或把元数据收敛到一处。
  • OpenAPI 版本不匹配:所有输入必须声明相同的openapi值。

两条路径怎么选

  • 你生成的是 Swagger 2.0(*.swagger.json),或依赖openapiv2_schema/openapiv2_operation等 v2 注解:用allow_merge=true,merge_file_name=...,一次生成即得到单文件。
  • 你生成的是 OpenAPI 3.1(*.openapi.json):v3 插件没有内置合并选项,必须用openapiv3-merge做后处理;同时注意它是 Alpha,且不支持 YAML 输出。

两条路径都只做文档层面的合并,不影响 gateway 的 HTTP 行为本身。

参考文档

  • Customizing OpenAPI Output — Merging output:allow_mergemerge_file_namepreserve_rpc_order的说明。
  • Merging OpenAPI 3.1 Output:openapiv3-merge的安装、用法与合并规则。
  • OpenAPI 3.1 Output:protoc-gen-openapiv3的 Alpha 状态、能力边界与选项。
  • openapiv3-merge README 与 merge 实现:字段级规则与严格模式的源码依据。

【免费下载链接】grpc-gatewaygRPC to JSON proxy generator following the gRPC HTTP spec项目地址: https://gitcode.com/GitHub_Trending/gr/grpc-gateway

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询