В обработчиках HTTP Go, почему ResponseWriter имеет значение, но запрашивает указатель?

Я учусь, написав приложение для GAE, и это подпись функции обработчика:

func handle(w http.ResponseWriter, r *http.Request) {}

Я новичок-указатель здесь, так почему объект Request указатель, но ResponseWriter нет? Есть ли необходимость в этом, или это просто сделать какой-то расширенный код на основе указателей?

Ответ 1

То, что вы получаете для w, является указателем на не экспортируемый тип http.response, но как ResponseWriter является интерфейсом, который не отображается.

От server.go:

type ResponseWriter interface {
    ...
}

С другой стороны, r является указателем на конкретную структуру, поэтому необходимо точно ее уточнить:

Из request.go:

type Request struct {
    ...
}

Ответ 2

http.ResponseWriter - это интерфейс, а существующие типы, реализующие этот интерфейс, являются указателями. Это означает, что нет необходимости использовать указатель на этот интерфейс, так как он уже "поддерживается" указателем. Эту концепцию немного описал один из разработчиков go здесь. Хотя тип, реализующий http.ResponseWriter, не обязательно должен быть указателем, он не будет практичным, по крайней мере, в пределах сервера go http.

http.Request - это не интерфейс, а просто структура, и, поскольку мы хотим изменить эту структуру и сделать так, чтобы веб-сервер видел эти изменения, это должен быть указатель. Если бы это было просто структурное значение, мы бы просто изменили его копию, которую веб-сервер, вызывающий наш код, не мог видеть.

Ответ 3

Причина, по которой указатель на Request прост: изменения обработчика Request должны быть видны серверу, поэтому они передавались только по ссылке, а не по значению.

Если вы покопаетесь в коде библиотеки net/http, вы обнаружите, что ResponseWriter является интерфейсом к неэкспортированному ответу структуры и передает структуру по ссылке (передавая указатель на ответ), а не по значению. ResponseWriter - это интерфейс, используемый обработчиком для создания ответа HTTP. Фактическое резервное копирование структуры ResponseWriter - это неэкспортированная структура http.response. Поскольку он не экспортируется, вы не можете использовать его напрямую; Вы можете использовать его только через интерфейс ResponseWriter.

Другими словами, оба параметра передаются по ссылке; Просто сигнатура метода принимает ResponseWriter, который является интерфейсом для указателя на структуру, поэтому он выглядит так, как будто он передан по значению.

Ответ 4

Как правильно упомянуто во многих других ответах здесь и в других местах, ResponseWriter является интерфейсом, и последствия этого подробно описаны в SO-ответах и блогах.

Я хотел бы остановиться на том, что я чувствую здесь большое - и опасное - неправильное представление о том, что причина запроса передается по "ссылке" (хотя на самом деле в Go такого нет) - "мы хотим внести изменения". ему виден сервер ".

Цитирую пару ответов от других людей:

[..] это просто структура, и поскольку мы хотим изменить эту структуру и сделать так, чтобы веб-сервер видел эти изменения, это должен быть указатель [..] fooobar.com/questions/54173/...

[..] изменения в запросе обработчиком должны быть видны серверу, поэтому они передавались только по ссылке, а не по значению [..] fooobar.com/questions/54173/...

Это неправильно; на самом деле документы прямо предупреждают о том, что они не могут изменить/изменить запрос:

За исключением чтения тела, обработчики не должны изменять предоставленный запрос.

Совсем наоборот, нет? :-)

Если вы хотите изменить запрос, например, добавьте заголовок трассировки, прежде чем передавать его следующему обработчику в цепочке промежуточного программного обеспечения, вы должны скопировать запрос и передать скопированную версию по цепочке.

Запросы на изменение поведения, чтобы разрешить изменение входящего запроса , были инициированы командой Go, но изменение чего-то подобного, вероятно, приведет к неожиданному, по крайней мере, некоторому существующему коду.

Зачем использовать указатель, если мы явно говорим людям не изменять запрос? Производительность, Request - это большая структура, и ее копирование может снизить производительность, особенно с учетом длинных цепочек промежуточного программного обеспечения. Команда должна была соблюсти баланс, определенно не идеальное решение, но здесь компромиссы явно на стороне производительности (а не безопасности API).

Ответ 5

Я думаю, что основной причиной для объекта Request, который должен быть передан как указатель, является поле Body. Для данного HTTP-запроса тело может читать только один раз. Если объект Request был клонирован, как и если бы он не был передан как указатель, у нас было бы два объекта с различной информацией о том, сколько было прочитано из тела.