Можно ли указать кастомный заголовок в HTTP запросе
Что проверяют
Умеете ли вы собрать запрос руками, а не только через http.Get, и знаете ли, что клиент делает с заголовками сам. Вопрос простой, поэтому оценивают детали вокруг него.
Как отвечать
Да. Заголовок ставится на запросе до отправки, поэтому нужна пара NewRequest плюс client.Do: у http.Get задать его негде.
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return err
}
req.Header.Set("X-Request-Id", id)
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()Что стоит назвать сверх кода. Set заменяет значение, Add добавляет второе с тем же ключом. Ключи канонизируются, x-request-id уедет как X-Request-Id, и если сервер требует свой регистр, пишут прямо в мапу заголовков. Host через заголовки не задаётся, для него отдельное поле запроса. При редиректе на другой хост клиент сам снимает чувствительные заголовки, включая Authorization. А если заголовок нужен на каждом запросе, например трассировочный, аккуратнее завести свой RoundTripper и подставить его в транспорт клиента.
Где спотыкаются
Используют &http.Client{} без поля Timeout. Таймаута по умолчанию нет вовсе, и один зависший сервер держит горутины, пока не кончится память. Заодно клиент создают на каждый вызов, теряя пул соединений.
Не дочитывают и не закрывают тело ответа. defer resp.Body.Close() нужен даже когда тело не интересует, иначе соединение не вернётся в пул и вместо переиспользования будет копиться новое.
Переносят привычку на серверную сторону и зовут w.Header().Set после первого Write или после WriteHeader. К этому моменту заголовки уже отправлены, вызов ничего не делает и ошибки не возвращает.