protoc命令必须分两次执行:先生成grpc代码,再单独生成gateway代码。因grpc-gateway插件仅读取已生成的.pb.go和_grpc.pb.go中的service定义,不解析protobuf语法,且依赖显式指定third_party/googleapis路径以识别google.api.http注解。

protoc 命令必须分两次执行,不能合并
很多人卡在第一步:用一条 protoc 命令同时加 --go_out、--go-grpc_out 和 --grpc-gateway_out,结果 your_service.pb.gw.go 文件为空,或报错 google/api/annotations.proto: File not found。
根本原因是:gRPC-Gateway 插件不负责解析 Protobuf 语法结构,它只读取已生成的 .pb.go 和 _grpc.pb.go 中的 service 定义,再据此生成 HTTP 路由逻辑。它依赖前序插件输出,不能越位。
- 先跑 gRPC 代码生成:
protoc -I . --go_out=. --go-grpc_out=. your_service.proto - 再单独跑 gateway 生成:
protoc -I . -I $GOPATH/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v2.15.2/third_party/googleapis/ --grpc-gateway_out=. --grpc-gateway_opt logtostderr=true your_service.proto - 路径中的
third_party/googleapis必须显式指定,否则google.api.http注解无法被识别——不是版本问题,是路径缺失
google.api.http 注解写错一个字符,HTTP 就 404
注解不是装饰器,它是 gateway 的唯一路由源。它直接决定方法、路径、参数绑定方式,且大小写、引号、字段名必须与 message 定义完全一致。
-
get:"/v1/users/{user_id}"要求 message 中存在uint32 user_id = 1;;若字段叫id却写成{user_id},gateway 解析失败,返回 404 -
body:"*"表示整个 JSON body 映射到入参 message;body:"user"则要求 JSON 顶层是{"user": {...}},否则字段丢失 - query 参数如
get:"/v1/users?status={status}",status必须是 message 的一级字段,嵌套字段(如filter.status)不支持自动展开 - 避免换行缩进写法:
option (google.api.http) = { post: "/v1/foo" };在某些 protoc 版本中会被静默忽略
RegisterXXXHandlerFromEndpoint 必须在 ListenAndServe 前调用
your_service.pb.gw.go 里只有一堆未注册的 handler 函数,不显式挂载,http.ServeMux 根本看不到任何路由。这不是配置问题,是根本没加载。
- 必须在
http.ListenAndServe或http.Serve之前调用RegisterYourServiceHandlerFromEndpoint -
ctx不能是短生命周期上下文(例如context.WithTimeout(ctx, 100ms)),注册过程可能涉及反射和类型检查,需要稳定上下文 - 常见错误:把注册逻辑放在 goroutine 里异步执行,或放在
main()尾部——此时 server 已启动,路由永远不生效
gateway 不是网关,只是协议转换层
gRPC-Gateway 本身不处理服务发现、限流、鉴权或动态路由,它只是一个静态的 REST-to-gRPC 翻译器。把它当“网关”用,容易在后期踩坑。
- 它不能 dial gRPC Server,也不支持 streaming;想代理 gRPC 后端,必须让后端自己暴露一个配套的 gateway sidecar(或内置),网关只跟这个 sidecar 通信
- JWT 鉴权不能只验 signature——必须校验
exp/nbf,并查本地缓存确认 token 是否仍在白名单(比如sync.Map+ TTL),否则会绕过登出和禁用状态 - 若需服务发现或限流,得额外集成 etcd + gobreaker + 手写路由匹配逻辑;gin 或 gorilla/mux 只能做前置入口,不能替代网关核心能力
.pb.gw.go 是否生成了非空文件,再检查 RegisterXXXHandlerFromEndpoint 是否真被执行,最后核对路径参数名是否拼写一致。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











