> Какова структура HTTP-ответа (Go)

Уровень: middle · Роль: backend · Категория: Технические вопросы

Компании: Ozon

Стек: Go

> Пример ответа

Короткий ответ

HTTP-ответ - это структурированное сообщение от сервера клиенту, состоящее из трёх частей: статусной строки (status line), заголовков (headers) и тела (body). Статусная строка содержит версию протокола, код статуса (например, 200 OK) и текстовое описание. Заголовки передают метаданные: Content-Type, Content-Length, Cache-Control и другие. Тело содержит полезную нагрузку - JSON, HTML, файл или пустоту. В Go структура ответа формируется через net/http, где статус и заголовки задаются до вызова Write.

Подробное объяснение

HTTP-ответ (response) - это сообщение, которое сервер отправляет клиенту после обработки запроса. Формат определён в RFC 7230 и выглядит так:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 27

{"message":"hello"}

Разбор по частям:

  1. Status line - первая строка: HTTP/1.1 200 OK. Содержит:

    • версию протокола (HTTP/1.1, HTTP/2);
    • трёхзначный код статуса (status code) - 1xx информационные, 2xx успех, 3xx редирект, 4xx ошибка клиента, 5xx ошибка сервера;
    • reason phrase - человекочитаемое описание (может быть пустым).
  2. Headers - блок строк вида Name: value, заканчивается пустой строкой. Ключевые заголовки:

    • Content-Type - формат тела (application/json, text/html);
    • Content-Length - размер тела в байтах;
    • Transfer-Encoding: chunked - если длина неизвестна заранее;
    • Set-Cookie, Cache-Control, Location (для 3xx) и другие.
  3. Body - опциональная часть, отделённая от заголовков пустой строкой. Может быть пустым (например, в 204 No Content или 304 Not Modified). В HTTP/2 тело передаётся в DATA frames, но логическая структура сохраняется.

В Go структура ответа управляется через http.ResponseWriter:

  • WriteHeader(code) - устанавливает статус (по умолчанию 200);
  • Header().Set(key, value) - задаёт заголовки до вызова Write;
  • Write(body) - записывает тело, автоматически устанавливая Content-Length, если тело записано одним вызовом.

На практике

Для backend-разработчика важно понимать, как правильно формировать ответы в Go:

  • Всегда устанавливайте Content-Type явно, иначе Go определит его через http.DetectContentType по первым 512 байтам тела - это может дать неожиданный результат.
  • Вызывайте WriteHeader только один раз - повторный вызов игнорируется и логируется.
  • Если вы пишете в ResponseWriter после WriteHeader, заголовки уже не изменятся.
  • Для JSON-ответов используйте json.NewEncoder(w).Encode(v) - это автоматически добавит Content-Type: application/json (если не задан) и корректно экранирует HTML.
  • Для потоковой передачи (streaming) используйте http.Flusher - при этом Content-Length не устанавливается, а используется chunked encoding.
  • Статус 204 No Content не должен иметь тела - Go сам уберёт Content-Length при пустом теле.

Пример кода

GO
package main
import (
"encoding/json"
"net/http"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
func handler(w http.ResponseWriter, r *http.Request) {
// Явно задаём заголовки до WriteHeader
w.Header().Set("Content-Type", "application/json")
w.Header().Set("X-Request-ID", r.Header.Get("X-Request-ID"))
user := User{ID: 1, Name: "Alice"}
// Статус по умолчанию 200, но можно задать явно
w.WriteHeader(http.StatusOK)
// Кодируем JSON прямо в ResponseWriter
if err := json.NewEncoder(w).Encode(user); err != nil {
// Если ошибка после WriteHeader - статус уже отправлен
http.Error(w, "internal error", http.StatusInternalServerError)
}
}
func main() {
http.HandleFunc("/user", handler)
http.ListenAndServe(":8080", nil)
}

Как отвечать на собеседовании

Начните с краткого перечисления трёх частей ответа, затем раскройте каждую. Обязательно упомяните:

  • разницу между status code и reason phrase;
  • роль заголовков Content-Type и Content-Length;
  • как Go обрабатывает порядок: заголовки → WriteHeader → Write;
  • поведение при повторном WriteHeader (игнорируется);
  • что происходит при записи тела без явного Content-Length (chunked или автоопределение).

Если спросят про HTTP/2 - отметьте, что структура логически та же, но передача идёт бинарными фреймами, а заголовки сжимаются HPACK.

Что проверяет интервьюер

Интервьюер оценивает:

  • базовое понимание протокола HTTP, а не только фреймворков;
  • знание порядка операций в Go при формировании ответа;
  • понимание edge cases: пустое тело, статусы без тела, потоковая передача;
  • умение объяснить, почему нельзя менять заголовки после WriteHeader;
  • знание типичных кодов статусов и их семантики.

Типичные ошибки

  • Запись заголовков после вызова Write или WriteHeader - они не применятся.
  • Установка Content-Length вручную при использовании json.Encoder - может привести к рассинхронизации.
  • Игнорирование ошибки от Encode - если соединение оборвалось, клиент получит обрезанный ответ.
  • Возврат 200 при ошибке бизнес-логики - нужно использовать 4xx/5xx.
  • Забывают про http.Error для простых текстовых ошибок - он сам устанавливает Content-Type и статус.
  • Путают WriteHeader(200) с Write - если вызвать Write без WriteHeader, статус будет 200, но это неявное поведение, лучше задавать явно.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью