Какова структура HTTP-ответа
Что проверяют
Понимаете ли вы, из чего состоит ответ на уровне протокола и как это отражается в net/http. Следом часто спрашивают, почему заголовок, выставленный после WriteHeader, не доходит до клиента, или чем отличается HTTP/2.
Как отвечать
В HTTP/1.1 ответ это текст из четырёх частей:
- статусная строка: версия, код и поясняющая фраза, например
HTTP/1.1 404 Not Found. Классы кодов: 1xx информационные, 2xx успех, 3xx перенаправления, 4xx ошибка клиента, 5xx ошибка сервера; - заголовки: пары
Имя: значение, по одной на строку, имена регистронезависимы. Типичные:Content-Type,Content-Length,Cache-Control,Set-Cookie,Location; - пустая строка (
\r\n): отделяет заголовки от тела; - тело: необязательно, его нет у ответа на
HEAD, у204и304. Длину задаётContent-LengthилиTransfer-Encoding: chunked.
В HTTP/2 и HTTP/3 смысл тот же, но ответ идёт бинарными фреймами HEADERS и DATA, статус становится псевдозаголовком :status, поясняющей фразы нет, заголовки сжимаются HPACK и QPACK.
Затем свяжите с Go. В обработчике важен порядок: w.Header().Set("Content-Type", "application/json"), потом w.WriteHeader(http.StatusCreated), потом w.Write(body). Первый Write без WriteHeader отправляет 200, а без Content-Type сервер угадает тип по первым 512 байтам через http.DetectContentType. Короткий ответ без Flush получает Content-Length автоматически, длинный уходит chunked.
Где спотыкаются
Меняют заголовки после WriteHeader или первого Write. Они уже отправлены, изменения молча теряются, а повторный WriteHeader оставляет в логе superfluous response.WriteHeader call.
Забывают defer resp.Body.Close() в клиенте или не дочитывают тело. Соединение не возвращается в пул keep-alive, и число открытых сокетов растёт.
Придают значение поясняющей фразе вроде OK. Клиент смотрит только на код, а в HTTP/2 фразы нет вовсе.