Go gRPC-Gateway XML转JSON中间件报错问题排查
问题原因
「invalid character '<' looking for beginning of value」错误的核心是gRPC-Gateway的内置JSON解析逻辑跑在了你的XML转JSON中间件前面。gRPC-Gateway默认会优先尝试解析请求体为JSON,但此时请求体还是原始XML格式,直接触发了解析失败。
解决办法
1. 调整中间件执行顺序,让XML转JSON逻辑先跑
在Go的HTTP路由链里,必须把你的XML转JSON中间件放在gRPC-Gateway注册的handler之前。举两个常见场景的写法:
// 场景1:针对单个路径挂载中间件 mux.Handle("/b2p/listen", xmlToJsonMiddleware(grpcGatewayHandler)) // 场景2:全局路由先挂载中间件,再注册gRPC-Gateway路由 mux := http.NewServeMux() // 若用gorilla/mux这类支持Use的路由库 mux.Use(xmlToJsonMiddleware) // 注册gRPC-Gateway的路由到mux _ = gw.RegisterYourServiceHandlerServer(ctx, mux, yourServer)
2. 补全XML转JSON中间件的关键逻辑
你的中间件必须做好三件事才能让gRPC-Gateway正常处理:
- 读取原始XML请求体,转成JSON格式
- 把请求的
Content-Type改成application/json,让gRPC-Gateway认为这是合法的JSON请求 - 替换请求体为转换后的JSON(因为HTTP请求体是只读流,必须重新设置)
给个可用的中间件示例(以goxml2json库为例,也可以用标准库encoding/xml+encoding/json自行实现转换):
func xmlToJsonMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // 只处理/b2p/listen路径的XML请求 if r.URL.Path != "/b2p/listen" || r.Header.Get("Content-Type") != "application/xml" { next.ServeHTTP(w, r) return } // 读取XML请求体 xmlBytes, err := io.ReadAll(r.Body) if err != nil { http.Error(w, "failed to read request body", http.StatusBadRequest) return } defer r.Body.Close() // XML转JSON xmlReader := strings.NewReader(string(xmlBytes)) jsonResult, err := xml2json.Convert(xmlReader) if err != nil { http.Error(w, "xml to json conversion failed", http.StatusBadRequest) return } // 重置请求体和头部 r.Body = io.NopCloser(jsonResult) r.Header.Set("Content-Type", "application/json") // 重新设置Content-Length r.ContentLength = int64(jsonResult.Size()) // 交给后续的gRPC-Gateway handler处理 next.ServeHTTP(w, r) }) }
3. 禁用gRPC-Gateway的自动解析(可选)
如果调整顺序后还是有问题,可以直接禁用gRPC-Gateway的内置请求体解析,自定义编组器来处理XML。注册handler时加上对应的选项:
// 自定义编组器,实现runtime.Marshaler接口,支持XML转gRPC消息 type customMarshaler struct{} func (cm *customMarshaler) Marshal(v interface{}) ([]byte, error) { // 实现JSON编组逻辑,可直接复用标准库 return json.Marshal(v) } func (cm *customMarshaler) Unmarshal(data []byte, v interface{}) error { // 这里可以直接用encoding/xml解析XML到gRPC结构体 xmlReader := strings.NewReader(string(data)) return xml.NewDecoder(xmlReader).Decode(v) } // 注册handler时传入选项 opts := []gw.ServeMuxOption{ gw.WithMarshalerOption(runtime.MIMEWildcard, &customMarshaler{}), gw.WithDisableRequestBuffering(), } _ = gw.RegisterYourServiceHandlerServer(ctx, mux, yourServer, opts...)
内容的提问来源于stack exchange,提问作者Cross
相关产品推荐
相关产品推荐

