golang中设置Host Header的小Tips
时间:2021-11-24 作者:smarteng 分类: Go语言
前言
最近在使用go的http包的时候遇到的问题:
client := &http.Client{}
req, _ := http.NewRequest("GET", url, nil)
//这样设置无效
res.Header.Set("Host", "xxx.xxx")
res, err := client.Do(req)
if err != nil {
return
}
defer res.Body.Close()
js, err = simplejson.NewFromReader(res.Body)
return
//设置Host需要这样处理
res.Host = "xxx.xxx"
笔者最近时间一直在学习和写Ruby和Go,尤其是Go,作为云计算时代的标准语言,写起来还是相当有感觉的,难过其会越来越火。
不过写的过程中,也遇到了一些小问题,本文就是分享关于go语言设置 HTTP请求当中 Host Header的一个小注意事项。
常规做法
通常我们在设置HTTP的Header请求时,一般都是这么做:
Header.Add("Authentization", "TOKEN")
Header.Add("Content-Type", "application/json")
...
Java, Ruby, Go 都是如此,区别的只是语法不同。但是对于Host Header的处理就不同了。在Go中,如果我们这么写:
header.Add("Host", "XXXXXXXXX")
那么问题就出来了 —— 也许从请求发送的log中,你看不到任何的错误,但是如果查看服务端的log,你会发现,服务端接受到Host Header并不是你想发送的,而仍然是URL中的Host。
这是为什么呢?
为什么Go中Host Header不能这么加?
原来Go的设计上是用一个单独的HOST属性来定义此Request属性,参考net/http包中Request.go文件定义的Request结构体:
// For server requests Host specifies the host on which the
// URL is sought. Per RFC 2616, this is either the value of
// the "Host" header or the host name given in the URL itself.
// It may be of the form "host:port".
//
// For client requests Host optionally overrides the Host
// header to send. If empty, the Request.Write method uses
// the value of URL.Host.
Host string
那么在使用上,如果我们想传一个特定的Host,应该这么做:
Req.Host="XXXX"
但是,为啥Go就如此特殊呢?
Go为啥如此特殊?
原来从HTTP Spec角度,是不大希望让Host Header可以修改,这里的HOST应该是从URL得到,而不是任意的指定。
但是考虑到很多场景,尤其是Client Requests,很多时候我们希望能够修改这个HOST参数,来模拟我们的需求,如果Go能响应修改其源码就好了?
搜一下,发现这个问题也确实有人提过,如下:
[net/http: Setting custom "Host" request header doesn't have effect #7682](net/http: Setting custom "Host" request header doesn't have effect #7682)
而结论是:
I don't think we can safely change the behavior at this point.
最好也只是更新了文档,去掉了关于HOST参数的歧义。
对Go实现的第三方工具的影响
既然go语言没有更改这个需求,为了适应大家的习惯,如果有必要,我们可以这样做一个Workaround:
if host := header.Get("Host"); host != "" {
req.Host = host
}