Add headers for each HTTP request using client

Issue

I know that I can add headers to each HTTP request manually using

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)

but I want to add this header automatically for each HTTP request in my app.

What is the best way to do it?

Solution

I’m aware of three possible solutions to this. In (my) order of preference:

  1. Wrap http.NewRequest with custom code that adds desired headers:

     func MyRequest(method, path string, 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
     }
    

    This approach has the advantage of being straight-forward, non-magical, and portable. It will work with any third-party software, that adds its own headers, or sets custom transports.

    The only case where this won’t work is if you depend on a third-party library to create your HTTP requests. I expect this is rare (I don’t recall ever running into this in my own experience). And even in such a case, perhaps you can wrap that call instead.

  2. Wrap calls to client.Do to add headers, and possibly any other shared logic.

     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
     }
    

    This approach is also straight-forward, and has the added advantage (over #1) of making it easy to reduce other boilerplate. This general method can also work very well in conjunction with #1. One possible draw-back is that you must always call your MyDo method directly, meaning you cannot rely on third party software which calls http.Do itself.

  3. Use a custom 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)
     }
    

    Then use it like this:

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

    This approach has the advantage of working "behind the scenes" with just about any other software, so if you rely on a third-party library to create your http.Request objects, and to call http.Do, this may be your only option.

    However, this has the potential disadvantage of being non-obvious, and possibly breaking if you’re using any third-party software which also sets a custom transport (without bothering to honor an existing custom transport).

Ultimately, which method you use will depend on what type of portability you need with third-party software. But if that’s not a concern, I suggest using the most obvious solution, which, by my estimation, is the order provided above.

Answered By – Flimzy

Answer Checked By – Gilberto Lyons (GoLangFix Admin)

Leave a Reply

Your email address will not be published.