You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 21:34:58