grpc gateway 是一个独立的 http 服务器,将 json/http 请求(如 post /v1/user)反向代理到本地 grpc 服务(如 user.get),不替代也不处理 grpc 流量;需先启动 grpc server 并正确配置 endpoint,再通过 protoc + grpc-gateway 插件生成 handler,注册时注意路径分离、cors 和 json 字段映射规则。

gRPC Gateway 是什么,它不替代 gRPC 服务
它不是“让 gRPC 接口自动变 HTTP”,而是另起一个 HTTP 服务器,把 POST /v1/user 这类请求反向代理到本地的 gRPC 服务(比如 User.Get)。gRPC 服务必须独立运行,grpc-gateway 只负责翻译和转发。
常见错误现象:rpc error: code = Unavailable desc = connection refused —— 八成是没先跑起 gRPC server,或者 grpc-gateway 指向了错的地址(比如用 localhost:8080 但 gRPC server 绑在 127.0.0.1:9090,Docker 环境里还可能跨 host)。
- 必须先启动 gRPC server,监听一个明确地址(推荐
127.0.0.1:9090,避免 IPv6 或 localhost 解析歧义) -
grpc-gateway启动时通过grpc.Dial连这个地址,不是连自己 - 生成的 HTTP handler 不处理 gRPC 流量,也不暴露
.proto文件 —— 它只响应 JSON/HTTP,不响应application/grpc
怎么生成 gateway stub:protoc + grpc-gateway 插件
核心命令是 protoc,不是写代码。你改了 .proto,就必须重跑这步,否则 HTTP 路由、参数绑定、JSON 字段名全都不对。
典型错误:漏装插件、路径没加进 PATH、或用了旧版 grpc-gateway(v2 要求 google/api/annotations.proto 必须带 http 规则,且 protoc-gen-openapiv2 已废弃)。
- 安装插件:
go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest - proto 文件里必须有
import "google/api/annotations.proto";和option (google.api.http) = { get: "/v1/users/{id}" }; - 生成命令示例:
protoc -I . -I $GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/v2/third_party/googleapis --grpc-gateway_out=logtostderr=true:. user.proto - 生成的
user.pb.gw.go里是 HTTP handler,不是 client —— 别把它当 gRPC client 用
怎么注册 gateway handler 到 HTTP server
别直接用 http.ListenAndServe,gateway handler 需要和 gRPC server 共享 context 生命周期,并支持 CORS、gzip、超时等中间件。标准做法是用 gorilla/mux 或原生 http.ServeMux 注册,再套一层 http.Handler 包装。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
容易踩的坑:runtime.NewServeMux() 默认不处理 OPTIONS 预检,前端发 CORS 请求直接 404;还有人把 gateway handler 和 gRPC server 注册到同一个端口但没做路径区分,结果 /healthz 走了 gateway、/grpc.health.v1.Health/Check 却 404。
- gateway handler 只挂载在 HTTP 路径上(如
/v1/*),gRPC server 单独监听另一个端口(如:9090) - 加 CORS 中间件:
cors.New(cors.Options{AllowedOrigins: []string{"*"}}).Handler(mux) - 注册前务必调用
gw.RegisterXXXHandlerFromEndpoint,传入的是 gRPC server 的 endpoint 地址,不是 gateway 自己的地址 - 如果要用 HTTPS,证书和 TLS 配置加在 HTTP server 层,gateway 层不感知
为什么 JSON 响应字段名不对,或嵌套结构崩了
因为 grpc-gateway 默认按 proto 字段的 json_name 选项或小驼峰规则转 JSON key,不是照抄 Go struct tag。如果你在 .proto 里写了 string user_name = 1;,默认 JSON 就是 user_name,不是 userName,除非你显式加 json_name: "user_name"。
更隐蔽的问题:proto message 里嵌套了另一个 message,但没在 http rule 里设 body: "*",结果 gateway 把整个 body 当成单个字段解析,报 cannot bind field "xxx" to body。
- 统一控制 JSON 字段名:在
.proto字段加json_name,例如string full_name = 1 [json_name = "fullName"]; - POST/PUT 请求想接收完整 body,必须在
httprule 里写明body: "*",否则 gateway 只从 query/path 提取字段 - enum 值默认序列化为数字,要字符串需加
(google.api.field_behavior) = OUTPUT_ONLY并配runtime.WithMarshalerOption - 空值处理依赖
runtime.WithForwardResponseOption,否则nil字段可能被忽略或填默认值
最常被忽略的一点:gateway 的 JSON 解析和 gRPC 的二进制 wire format 是两套逻辑,哪怕字段名一致,类型不匹配(比如 proto 里是 int32,HTTP body 传了字符串 "123")也会静默失败或返回 400,调试时得看 gateway 日志里的 binding error,不是看 gRPC server 日志。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










