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.json,protoc-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_merge和merge_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.protomerge_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按字典序排列,保证结果确定。
输入顺序很重要:合并文档的info、servers、externalDocs取自第一个输入,后面输入的这些字段会被丢弃。所以要把范围最宽、承载共享openapiv3_document注解(title/version/servers 等)的那份文件放最前。如果没有任何文件被指定为"根",文档的建议是全部按字典序排序、接受排序靠前的那份——但要在对应 proto 上设置openapiv3_document注解,合并结果的title、version、servers才有合理的值。
对于任意输入都能排第一的简单目录,也有 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 声明顺序(
openapi、info、servers、paths、webhooks、components、security、tags、externalDocs,再跟扩展字段),paths/webhooks按输入顺序输出,components/*子图按 key 排序。
openapiv3-merge 的合并规则与冲突排查
合并器是严格模式:任何可能被静默覆盖的地方都会报错。字段级规则如下(来自 docs/docs/mapping/openapi_v3_merge.md):
| 字段 | 规则 |
|---|---|
openapi | 所有输入必须一致 |
info、servers、externalDocs | 取第一个输入,后续输入的值被丢弃 |
paths、webhooks | 按输入顺序并集;同一路径内容不一致 → 报错 |
components/* | 并集,key 排序输出;同名内容不一致 → 报错 |
tags | 按name去重;同名但元数据不一致 → 报错 |
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_merge、merge_file_name、preserve_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),仅供参考