Golang 操作 HTTP Header 的一个小细节


Golang 操作 HTTP Header 的一个小细节

在平时开发中发送 HTTP 请求是非常频繁的操作,对于 HTTP Header 的操作也是很常用的操作。今天在 Review 一段代码的时候,发现了一个小问题,事后想想其实是很小的问题,很可能都不会影响运行结果,但是都是如果没有遇到过很可能就不会注意到的一些问题,所以作为复盘记录,把这些小细节整理出来。

Header.Add 还是 Header.Set ?

这个问题起始于一段代码(仅保留相关部分):

<code>http.HandleFunc("/forum/correlation", RegForumCorrelation)
func RegForumCorrelation(w http.ResponseWriter, r *http.Request) {
// ... 省略业务代码 ...
// 发送 HTTP 请求
req, err := http.NewRequest("POST", bussinessURL, reqBody)
if err != nil {
// error handle
}
req.Header.Add("X-Authorization", authorizationSign)
req.Header.Add("Content-Type", "application/json")
// ...
}/<code>

在这里,我看到这个开发者使用了 Header.Add 来设置请求的 Header,我询问开发者这里为什么用 Add 而不用 Set,得到的回答是这两个方法得到的结果是一样的。那么,这里使用 Add 方法对么,或者说合适么?

如果说从结果来看,这里并不会有什么问题,因为这里只是一次性的设置,然后发送请求,目前的实现方法完全可以达到目的而且不会有问题。那么是否就意味着 Header.Add 和 Header.Set 没有什么区别呢,当然不是。所以这里我们本着

要想彻底解决问题就要从源码探究的原则来看下这个问题。

其实源码中的注释已经介绍的很明显了,这里不再赘述,我们主要看代码。

<code>type MIMEHeader map[string][]string

func (h MIMEHeader) Add(key, value string) {
key = CanonicalMIMEHeaderKey(key)
h[key] = append(h[key], value)
}

func (h MIMEHeader) Set(key, value string) {
h[CanonicalMIMEHeaderKey(key)] = []string{value}
}/<code>

上面就是 Add 和 Set 两个方法的源码,一目了然。Header 是一个 key 为 string,value 为 []string 的 map 类型。其中 key 为 Header 的名称,value 为 Header 对应名称的值,是一个数组。到这里 Add 和 Set 的区别就显而易见了,Add 向 value 的数组中追加一个值,Set 则重写这个值,覆盖掉已有的。

然而这里其实还有个小细节,就是 Header.Get 方法,既然 Header 中 key 对应的 value 是一个 []string 类型,那么 Get 方法获取到的也是这个么?其实并不是:

<code>func (h MIMEHeader) Get(key string) string {
if h == nil {
return ""
}
v := h[CanonicalMIMEHeaderKey(key)]
if len(v) == 0 {
return ""
}
return v[0]
}/<code>

通过源码可以看到,使用 Get 方法只能够获取到切片中的第一个值,如果想要获取其他的值,只能通过直接访问 Header 的 map 结构去获取了。当然这些也已经在 Get 方法的注释中提到了。所以这里也可以提一个习惯,使用标准库的时候,最好能够详细看看方法的注释,毕竟官方的注释还是很详细的。

在大多数 Get 可以满足需求的时候,我们还是更推荐使用 Get 方法。在源码中有一个登场率很高的函数 CanonicalMIMEHeaderKey,Header 名称在 Add,Set,Get 的时候都会经过这个函数,不知道大家有没有注意过,我们在通过 Add,Set 设置 Header 的时候,Header 的名称都是大小写不敏感的,而在我们接收到的 Header 中却都是规范的。其实这就是 CanonicalMIMEHeaderKey 的作用,把传入的字符串类型参数的首字母大写,然后其他连接词的首字母大写,其余的都小写。例如 Content-Type,User-Agent。

所以如果说在实际的开发中 Get 方法无法满足需求,需要直接访问 Header 的 map 结构,那么一定要注意这一点。

欢迎大家能够交流自己的一些问题和经验,持续学习,共同成长!


分享到:


相關文章: