'Add headers for each HTTP request using client
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 1:[1]
I'm aware of three possible solutions to this. In (my) order of preference:
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.
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 callshttp.Do
itself.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 callhttp.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.
Solution 2:[2]
It's possible to configure http.Client
to use custom transport, which can handle each request in the client (found this implementation in golang.org/x/oauth2 library). This example appends headers to each http request:
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)
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 |