Всегда ли в HTTP-запросе есть тело
Что проверяют
Понимаете ли вы разницу между «тела нет» и «тело пустое», и знаете ли, как это выглядит на стороне сервера в Go.
Как отвечать
Нет, не всегда. Тело необязательно, и у большинства GET, HEAD, DELETE и OPTIONS его нет: данные передаются в пути и в параметрах запроса. У POST, PUT и PATCH тело обычно есть, но формально и оно не обязано быть.
Что определяет наличие тела на уровне протокола: заголовки Content-Length или Transfer-Encoding: chunked. Спецификация не запрещает тело у GET, но и не придаёт ему смысла, а на практике прокси, кеши и часть серверов такое тело отбрасывают, поэтому полагаться на него нельзя.
Специфика Go, ради которой вопрос и задают:
- на сервере
r.Bodyникогда не равенnil. Если тела нет, там лежитhttp.NoBody, и чтение сразу даётio.EOF. Проверятьif r.Body != nilбессмысленно, проверяют результат чтения; - закрывать тело на сервере не нужно, это делает сам сервер, а вот на клиенте
defer resp.Body.Close()обязателен, иначе соединение не вернётся в пул; - тело читается один раз: это поток. Нужно прочитать дважды (например, для журнала и для разбора), содержимое буферизуют и подменяют
r.Bodyнаio.NopCloser(bytes.NewReader(buf)); - размер стоит ограничивать через
http.MaxBytesReader, иначе большой запрос съест память; - у ответов свои правила: на
HEADи на статусы 204 и 304 тела не бывает.
Где спотыкаются
Проверяют r.Body != nil вместо чтения и делают вывод, что тело есть всегда.
Не закрывают тело ответа на клиенте и получают утечку соединений под нагрузкой.
Пытаются разобрать тело второй раз и получают пустую структуру без ошибки, потому что поток уже вычитан.