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 結構,那麼一定要注意這一點。

歡迎大家能夠交流自己的一些問題和經驗,持續學習,共同成長!


分享到:


相關文章: