【问题标题】:Add headers for each HTTP request using client使用客户端为每个 HTTP 请求添加标头
【发布时间】:2019-01-08 09:20:21
【问题描述】:

我知道我可以使用手动向每个 HTTP 请求添加标头

cli := &http.Client{}
req, err := http.NewRequest("GET", "https://myhost", nil)
req.Header.Add("X-Test", "true")
if err != nil {
    panic(err)
}
rsp, err := cli.Do(req)

但我想为我的应用程序中的每个 HTTP 请求自动添加此标头。

最好的方法是什么?

【问题讨论】:

  • 为什么不简单地把它包装成一个函数来发出请求呢?
  • 包装可以工作,只要你是直接使用它的人。但是一旦你不得不将一个客户端交给另一个以自己的方式使用它的库,包装就不够了。
  • 当然,我可以用自定义函数包装 client.Do 以附加标头,但 client.Getclient.Post 看起来比 client.NewRequest 更简洁,错误检查,然后是 myDo 函数。跨度>

标签: go http-headers go-http


【解决方案1】:

我知道三种可能的解决方案。按(我的)偏好顺序:

  1. 使用添加所需标头的自定义代码包装 http.NewRequest

    func MyRequest(method, path url, body io.Reader) (*http.Request, error) {
        req, err := http.NewRequest(method, path, body)
        if err != nil {
            return nil, err
        }
        req.Header.Add("X-Test", "true")
        return req, nil
    }
    

    这种方法的优点是直截了当、非神奇且可移植。它适用于任何添加自己的标头或设置自定义传输的第三方软件。

    这不起作用的唯一情况是您依赖第三方库来创建您的 HTTP 请求。我希望这很少见(我不记得在我自己的经验中遇到过这种情况)。即使在这种情况下,也许你可以包装 that 调用。

  2. 包装对client.Do 的调用以添加标头以及可能的任何其他共享逻辑。

    func MyDo(client *http.Client, req *http.Request) (*http.Response, error) {
        req.Header.Add("X-Test", "true")
        // Any other common handling of the request
        res, err := client.Do(req)
        if err != nil {
            return nil, err
        }
        // Any common handling of response
        return res, nil
    }
    

    这种方法也是直截了当的,并且具有额外的优势(超过#1),可以轻松减少其他样板。这种通用方法也可以与#1 一起很好地工作。一个可能的缺点是您必须始终直接调用 MyDo 方法,这意味着您不能依赖调用 http.Do 本身的第三方软件。

  3. 使用自定义http.Transport

    type myTransport struct{}
    
    func (t *myTransport) RoundTrip(req *http.Request) (*http.Response, error) {
        req.Header.Add("X-Test", "true")
        return http.DefaultTransport.RoundTrip(req)
    }
    

    然后像这样使用它:

    client := &Client{Transport: &myTransport{}}
    req := http.NewRequest("GET", "/foo", nil)
    res, err := client.Do(req)
    

    这种方法的优势在于几乎可以与任何其他软件“在幕后”工作,因此如果您依赖第三方库来创建您的http.Request 对象并调用http.Do,这可能是你唯一的选择。

    但是,这具有不明显的潜在缺点,如果您使用任何也设置自定义传输的第三方软件(无需费心遵守现有的自定义传输),则可能会破坏。

最终,您使用哪种方法将取决于您需要哪种类型的第三方软件可移植性。但如果这不是问题,我建议使用最明显的解决方案,据我估计,它是上面提供的顺序。

【讨论】:

    【解决方案2】:

    可以将http.Client 配置为使用自定义传输,它可以处理客户端中的每个请求(在golang.org/x/oauth2 库中找到此实现)。此示例将标头附加到每个 http 请求:

    type transport struct {
        headers map[string]string
        base    http.RoundTripper
    }
    
    func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {
        for k, v := range t.headers {
            req.Header.Add(k, v)
        }
        base := t.base
        if base == nil {
            base = http.DefaultTransport
        }
        return base.RoundTrip(req)
    }
    
    func main() {
        cli := &http.Client{
            Transport: &transport{
                headers: map[string]string{
                    "X-Test": "true",
                },
            },
        }
        rsp, err := cli.Get("http://localhost:8080")
        defer rsp.Body.Close()
        if err != nil {
            panic(err)
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-11-25
      • 1970-01-01
      • 2021-05-13
      • 2010-12-06
      • 1970-01-01
      • 2015-05-17
      • 2017-06-11
      • 1970-01-01
      相关资源
      最近更新 更多