Почему в обработчиках HTTP на Go ResponseWriter - значение, а указатель - Request?


84

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

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

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

Ответы:


66

Вы получаете wуказатель на неэкспортированный тип, http.responseно, как ResponseWriterи интерфейс, он не виден.

С server.go :

type ResponseWriter interface {
    ...
}

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

Из request.go :

type Request struct {
    ...
}

28

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

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


1
Я не думаю, что это правильно. Значения за указателями могут реализовывать интерфейсы так же, как и значения, поэтому здесь нет необходимости проводить различие. Тип с базовым типом int может удовлетворять интерфейсу, не являясь указателем и не поддерживаясь им.
nemo

2
Ну, я не имел в виду, что все, что реализует интерфейс, должно быть указателями. Что-то вроде ResponseWriter, которому нужно будет изменить состояние значения за интерфейсом, чтобы сделать что-нибудь полезное, и для этого потребуется, чтобы это значение было типом указателя (как также говорится в сообщении в блоге, на которое я ссылался). Но да, http.ResponseWriter теоретически может быть реализован с помощью int (чьи методы никогда не могут изменить это значение int).

Этот ответ был очень полезным. Эта ссылка бесценна, с четким объяснением для тех из нас, кто плохо знаком с указателями. «То есть, хотя явного маркера нет, объекты интерфейса часто действуют как указатели. Это может сбивать с толку, пока вы не поймете, что на самом деле происходит».
Коди Джанго

1
Часть о
joakim

7

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

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

Процитирую пару ответов:

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

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

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

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

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

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

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

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


1
Это ответ, который наконец обрел для меня смысл.
Джими

2

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

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

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


Для меня имеет больше смысла, чем исходный ответ
Venkata SSKM Chaitanya

Часть о
joakim

0

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.