Добавьте заголовки для каждого HTTP-запроса с помощью клиента
Я знаю, что могу добавить заголовки к каждому 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-запроса в моем приложении.
Каков наилучший способ сделать это?
2 ответа
Я знаю о трех возможных решениях этого. В (моем) порядке предпочтения:
Заворачивать
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-запросов. Я ожидаю, что это редко (я не помню, чтобы когда-либо сталкивался с этим в моем собственном опыте). И даже в таком случае, возможно, вы можете вместо этого обернуть этот вызов.
Оберните звонки
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
сам.Используйте кастом
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
, это может быть вашим единственным вариантом.Тем не менее, это имеет потенциальный недостаток, заключающийся в том, что он неочевиден и, возможно, ломается, если вы используете любое стороннее программное обеспечение, которое также устанавливает пользовательский транспорт (не заботясь о существующем пользовательском транспорте).
В конечном итоге, какой метод вы используете, будет зависеть от того, какой тип переносимости вам нужен со сторонним программным обеспечением. Но если это не проблема, я предлагаю использовать наиболее очевидное решение, которое, по моей оценке, является приведенным выше порядком.
Можно настроить http.Client
использовать пользовательский транспорт, который может обрабатывать каждый запрос клиента (эту реализацию можно найти в библиотеке https://godo c.org/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)
}
}