Go语言中如何将动态json.RawMessage转为net/http请求头?
我正在开发一个“fire and forget”微服务,它接收HTTP POST请求,从请求体中提取数据后,向指定URL发起请求。当前遇到的问题是:输入请求体中的请求头数量不固定,可能有1个或多个。
我现在将请求体反序列化为以下Go结构体:
type Request struct { Headers json.RawMessage `json:"headers"` Method string `json:"method"` Body json.RawMessage `json:"body"` } type Event struct { Url string `json:"url"` Request Request `json:"request"` }
反序列化代码如下:
var newEvent Event reqBody, err := ioutil.ReadAll(r.Body) if err != nil { fmt.Fprintln(w, err) } err2 := json.Unmarshal(reqBody, &newEvent) if err2 != nil { fmt.Fprintln(w, err2) }
创建目标请求时,请求体可以直接传递:
req, err := http.NewRequest(newEvent.Request.Method, newEvent.Url, bytes.NewBuffer(newEvent.Request.Body)) if err != nil { panic(err) }
但设置请求头时,无法直接使用json.RawMessage格式,理想状态是能直接执行类似req.Header = http.Header{newEvent.Request.Headers}的操作,请问有没有更巧妙的解决方案?还是必须将头反序列化为字符串后解析,再用http.Header.Add()逐个添加?
方法一:直接将Headers定义为http.Header类型
这是最简洁的方案,因为http.Header本质是map[string][]string,Go的JSON序列化器可以直接将JSON对象反序列化为这个类型,不需要用json.RawMessage中转。
修改结构体:
type Request struct { Headers http.Header `json:"headers"` Method string `json:"method"` Body json.RawMessage `json:"body"` } type Event struct { Url string `json:"url"` Request Request `json:"request"` }
反序列化完成后,直接赋值请求头即可:
req.Header = newEvent.Request.Headers
注意:HTTP头字段名是大小写不敏感的,
http.Header会自动将键转换为规范格式(比如content-type转为Content-Type);同时如果JSON中的头值是单个字符串(而非数组),http.Header的UnmarshalJSON方法会自动将其转为长度为1的切片,完全符合HTTP头的格式要求。
方法二:保留json.RawMessage,二次反序列化
如果因为某些原因必须保留json.RawMessage类型的Headers字段,可以将其二次反序列化为http.Header:
var headers http.Header err := json.Unmarshal(newEvent.Request.Headers, &headers) if err != nil { // 处理反序列化错误 } req.Header = headers
如果JSON中的头值存在单个字符串/数组混合的情况,也可以先反序列化为map[string]interface{}再转换:
var headerMap map[string]interface{} err := json.Unmarshal(newEvent.Request.Headers, &headerMap) if err != nil { // 处理反序列化错误 } headers := make(http.Header) for key, val := range headerMap { switch v := val.(type) { case string: headers.Add(key, v) case []interface{}: for _, item := range v { if s, ok := item.(string); ok { headers.Add(key, s) } } } } req.Header = headers
不推荐逐个添加的方式
除非有特殊的自定义处理逻辑,否则不需要用http.Header.Add()逐个添加,上述两种方法都能更高效地完成请求头的设置。
内容的提问来源于stack exchange,提问作者Maxxxxxx

