gRPC Gateway 404/503 根本原因是 handler 未注册到独立 runtime.ServeMux;proto 注解需正确 import 并生成 pb.gw.go;路径参数名须与 message 字段名严格一致;JSON 序列化需显式配置 protojson.MarshalOptions。

gRPC Gateway 404 或 503 的根本原因不是配置错,而是没注册 handler
它不会自动把 RegisterXXXHandlerFromEndpoint 注入到 HTTP 路由里——你得亲手把它挂到 http.ServeMux 上,而且不能塞进 Gin/Echo 的中间件链里。
-
runtime.NewServeMux()必须是独立实例,不是复用http.DefaultServeMux;传nil给http.ListenAndServe就等于走默认 mux,里面啥都没有 - 常见错误写法:
r.POST("/v1/hello", gwMux.ServeHTTP)—— 这丢掉了 method 和 path 匹配逻辑,ServeHTTP只是兜底函数,不负责路由分发 - 正确做法:先调用
example.RegisterGreeterHandlerFromEndpoint(ctx, gwMux, "localhost:9090", opts),再用http.ListenAndServe(":8080", gwMux)
proto 中的 google.api.http 注解不生效?那是因为没生成 pb.gw.go
注解只是声明,不是魔法。它只在代码生成阶段起作用,且依赖两个硬性条件:正确的 import 和显式启用插件。
- proto 文件里必须有这两行:
import "google/api/annotations.proto";和import "google/api/http.proto"; -
protoc命令必须带--grpc-gateway_out参数,例如:protoc --grpc-gateway_out=paths=source_relative:. service.proto - 生成的
service.pb.gw.go文件必须被 Go 项目 import(哪怕只是 blank import:_ "your/project/proto"),否则 handler 根本不编译进去
GET /users/{id} 返回 404?路径参数名和字段名必须一字不差
gRPC-Gateway 不做模糊匹配或类型转换。它靠字段名严格绑定 URL 路径段,大小写、下划线、嵌套层级全算数。
- message 定义必须是顶层字段:
message GetUserRequest { string id = 1; },不能是message Wrapper { User user = 1; }里再嵌一层 -
option (google.api.http) = { get: "/v1/users/{id}" }里的{id}必须和 message 字段名id完全一致(ID、user_id都不行) - 如果字段是
int32 id = 1;,但请求 URL 是/v1/users/123,Gateway 会静默失败返回 404 —— 它不尝试字符串转整数,建议统一用string id
JSON 返回字段缺失或时间格式不对?别用默认序列化选项
新版 google.golang.org/protobuf/encoding/protojson 默认不输出零值字段(如 int32 status = 1; 值为 0 时前端收不到 status),且 google.protobuf.Timestamp 默认转成 RFC3339 字符串,和某些前端库预期不一致。
立即学习“go语言免费学习笔记(深入)”;
- 初始化
runtime.ServeMux时要传自定义 marshaler:runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{...})已弃用,改用runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{...})不再推荐;应直接用protojson.MarshalOptions - 想输出零值字段,必须设:
protojson.MarshalOptions{EmitUnpopulated: true} - 时间字段若需毫秒级 Unix 时间戳(而非字符串),得在 proto 中避免用
google.protobuf.Timestamp,改用int64 created_at = 1;并自行处理


















