使用 buf + protoc-gen-doc 快速生成接口文档,轻松管理成百上千的 gRPC/HTTP 路由
背景
在微服务架构中,gRPC 因其高性能而成为服务间通信的首选,同时通过 grpc-gateway 可对外暴露 HTTP/JSON 接口,兼顾内外调用。随着业务发展,接口数量可能迅速增长到几百甚至上千个。如何高效管理这些接口?如何将接口定义导入到 API 管理平台或前端展示?手动编写文档显然不现实,而直接使用 .proto 文件又缺乏灵活的筛选和结构化输出。
本文将介绍一种轻量级自动化方案:利用 buf CLI 和 protoc-gen-doc 插件,结合自定义模板,一键生成精简的接口路由文档(JSON 格式),方便增量或全量导入数据库,为后续 API 治理、前端调试提供坚实基础。
Buf CLI:现代化的 Protobuf 工具链
buf 是一个强大的 Protobuf 工具链,旨在解决原生 protoc 复杂、依赖管理难等问题。它提供了:
- lint:检查 Proto 文件规范
- generate:统一的代码生成入口
依赖管理:通过 buf.yaml 管理远程依赖
使用 buf,我们可以将代码生成配置集中在一个 buf.gen.yaml 文件中,轻松集成各种插件,包括本文的主角 protoc-gen-doc。
protoc-gen-doc:从 Proto 生成文档
protoc-gen-doc 是一个 protoc 插件,能够从 .proto 文件生成 HTML、Markdown、JSON 等格式的文档。其核心能力是解析 Proto 文件中的服务、方法、消息、枚举,以及自定义选项(如 google.api.http),并将它们以结构化形式输出。
默认的 JSON 输出包含所有信息,体积庞大。对于只想提取“服务名、方法名、HTTP 路由”等核心字段的场景,我们需要更精简的数据。
痛点:三四百个接口,如何高效入库?
假设你有三四百个 gRPC 接口,并通过 grpc-gateway 暴露了对应的 HTTP 接口。现在需要将这些接口信息(包括 gRPC 全限定名、HTTP 方法、路径、描述等)导入数据库,供前端 API 管理后台使用。
- 手工提取:效率低且容易出错。
- 直接使用 swagger.json:只能覆盖 HTTP 部分,丢失原生 gRPC 信息。
使用 protoc-gen-doc 全量 JSON:包含大量消息定义,数据冗余,前端解析复杂。
理想的方案是:一次生成,输出干净、只包含必要字段的 JSON 文件,前端直接 import 或后端直接入库。
解决方案:buf + protoc-gen-doc + 自定义模板
整体流程非常简单:
- 编写 buf.gen.yaml 配置,声明使用 protoc-gen-doc 插件,并指定自定义模板。
- 编写一个 Go 模板文件(.tmpl),描述你想要的 JSON 结构。
- 运行 buf generate,瞬间得到精简后的接口文档 JSON。
步骤一:配置 buf.gen.yaml
...
plugins:
...
- local: protoc-gen-doc
out: ./docs # 输出目录
opt: tmpl/protoc-gen-doc.tmpl,apis.json # 模板路径 + 输出文件名
...
这里 local: protoc-gen-doc 表示使用本地安装的插件(需提前 go install)。
步骤二:编写自定义模板
假设我们希望生成的 JSON 格式如下:
{
"files": [
{
"name": "path/to/file.proto",
"package": "your.package.v1",
"services": [
{
"name": "YourService",
"methods": [
{
"name": "YourMethod",
"fullName": "/your.package.v1.YourService/YourMethod",
"description": "方法描述",
"httpRule": {
"method": "POST",
"path": "/v1/your/path"
}
}
]
}
]
}
]
}
根据 protoc-gen-doc 的内部数据结构,我们可以编写对应的 Go 模板。核心逻辑:
- 遍历 .Files,提取文件名和包名。
- 遍历 Services 及其 Methods。
- 从 method.Options 中获取 google.api.http 选项,解析出 Rules(HTTP 绑定规则),取第一条规则的 Method 和 Pattern。
// tmpl/protoc-gen-doc.tmpl
{
"files": [
{{- range $i, $file := .Files }}
{{- if $i }},{{ end }}
{
"name": "{{ $file.Name }}",
"package": "{{ $file.Package }}",
"services": [
{{- range $j, $service := $file.Services }}
{{- if $j }},{{ end }}
{
"name": "{{ $service.Name }}",
"methods": [
{{- range $k, $method := $service.Methods }}
{{- if $k }},{{ end }}
{
"name": "{{ $method.Name }}",
"fullName": "{{ $service.FullName }}/{{ $method.Name }}",
"description": "{{ $method.Description }}",
{{- $httpOption := index $method.Options "google.api.http" }}
{{- if $httpOption }}
{{- $rules := $httpOption.Rules }}
{{- if and $rules (gt (len $rules) 0) }}
{{- $firstRule := index $rules 0 }}
"httpRule": {
"method": "{{ $firstRule.Method }}",
"path": "{{ $firstRule.Pattern }}"
}
{{- else }}
"httpRule": null
{{- end }}
{{- else }}
"httpRule": null
{{- end }}
}
{{- end }}
]
}
{{- end }}
]
}
{{- end }}
]
}
步骤三:运行生成
buf generate
等待一会后,docs/apis.json 便包含了所有你需要的接口信息,且无冗余数据。
你可以将此 JSON 文件纳入版本控制,当增加/修改/删除接口,可以通过ai去提取新增/修改/删除的接口。
更多推荐




所有评论(0)