W programach obsługi Go HTTP, dlaczego ResponseWriter jest wartością, ale Request a wskaźnik?

84

Uczę się Go, pisząc aplikację dla GAE, a to jest sygnatura funkcji obsługi:

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

Jestem początkującym wskaźnikiem, więc dlaczego Requestobiekt jest wskaźnikiem, ale ResponseWriternie? Czy jest jakakolwiek potrzeba, aby to mieć w ten sposób, czy jest to tylko po to, aby umożliwić jakiś rodzaj zaawansowanego kodu opartego na wskaźnikach?

Sudhir Jonathan
źródło

Odpowiedzi:

66

Otrzymujesz wwskaźnik do nieeksportowanego typu, http.responseale podobnie jak ResponseWriterinterfejs, nie jest on widoczny.

Z server.go :

type ResponseWriter interface {
    ...
}

Z drugiej strony rjest wskaźnikiem do konkretnej struktury, stąd potrzeba jawnego przekazania referencji.

Z request.go :

type Request struct {
    ...
}
Denys Séguret
źródło
28

http.ResponseWriterJest interfejsem, a istniejące typy implementujące ten interfejs są wskaźnikami. Oznacza to, że nie ma potrzeby używania wskaźnika do tego interfejsu, ponieważ jest on już „wspierany” przez wskaźnik. Ta koncepcja jest opisana trochę przez jednego z programistów go tutaj. Chociaż typ implementujący http.ResponseWriter nie musiał być wskaźnikiem, nie byłby praktyczny, przynajmniej nie w obrębie serwera go http.

http.Requestnie jest interfejsem, to tylko struktura, a ponieważ chcemy zmienić tę strukturę i pozwolić serwerowi WWW zobaczyć te zmiany, musi to być wskaźnik. Gdyby była to tylko wartość struktury, po prostu zmodyfikowalibyśmy jej kopię, której serwer sieciowy wywołujący nasz kod nie mógł zobaczyć.

nr
źródło
1
Myślę, że to nie jest w porządku. Wartości za wskaźnikami mogą implementować interfejsy tak samo, jak wartości, więc nie ma potrzeby ich rozróżniania. Typ z typem bazowym int może spełnić wymagania interfejsu bez bycia wskaźnikiem lub popierania przez jeden.
nemo
2
Cóż, nie chciałem sugerować, że wszystkie rzeczy implementujące interfejs muszą być wskaźnikami. Coś w rodzaju ResponseWriter, który musiałby zmodyfikować stan wartości za interfejsem, aby zrobić cokolwiek użytecznego, i który będzie wymagał, aby wartość była typem wskaźnika (jak mówi również post na blogu, do którego utworzyłem łącze). Ale tak, http.ResponseWriter mógłby teoretycznie zostać zaimplementowany przez int (którego metody nigdy nie mogłyby zmienić tej wartości int).
nr
Ta odpowiedź była bardzo pomocna. To połączenie jest bezcenne, z jasnym wyjaśnieniem dla tych z nas, którzy nie znają wskazówek. „Oznacza to, że chociaż nie ma wyraźnego znacznika, obiekty interfejsu często zachowują się jak wskaźniki. Może to być mylące, dopóki nie zrozumiesz, co się naprawdę dzieje”.
Cody Django
1
Część dotycząca żądania jest rzeczywiście nieprawidłowa, zobacz moją odpowiedź stackoverflow.com/a/56875204/989991
joakim.
7

Jak słusznie wspomniano w wielu innych odpowiedziach tutaj i gdzie indziej, ResponseWriterinterfejs, a jego konsekwencje zostały szczegółowo opisane w odpowiedziach SO i na blogach.

Chciałabym odnieść się do tego, co uważam za duże - i niebezpieczne - błędne przekonanie, że powód żądania jest przekazywany przez „odniesienie” (chociaż coś takiego nie istnieje w Go ) jest takie, że „chcemy wprowadzić zmiany widoczne dla serwera ”.

Cytując kilka odpowiedzi:

[..] to tylko struktura, a ponieważ chcemy zmienić tę strukturę i pozwolić serwerowi WWW zobaczyć te zmiany, musi to być wskaźnik [..] SO

[..] zmiany żądania przez procedurę obsługi muszą być widoczne dla serwera, więc przekazujemy je tylko przez odwołanie, a nie przez wartość [..] SO

To jest złe ; w rzeczywistości dokumenty wyraźnie ostrzegają przed manipulowaniem / modyfikowaniem żądania :

Z wyjątkiem czytania treści, osoby obsługujące nie powinny modyfikować dostarczonego Request.

Wręcz przeciwnie, nie? :-)

Jeśli chcesz zmienić żądanie, np. Dołączyć nagłówek śledzenia przed przekazaniem go do następnego programu obsługi w łańcuchu oprogramowania pośredniego , musisz skopiować żądanie i przekazać skopiowaną wersję w dół łańcucha.

Żądania zmiany zachowania, aby umożliwić modyfikacje przychodzącego żądania , zostały zgłoszone przez zespół Go, ale zmiana czegoś takiego prawdopodobnie doprowadziłaby do nieoczekiwanego uszkodzenia przynajmniej części istniejącego kodu.

Po co używać wskaźnika, jeśli wyraźnie mówimy ludziom, aby nie modyfikowali żądania? Wydajność , Requestjest duża struktura i kopiowanie może przynieść wyniki w dół, zwłaszcza z długich łańcuchów middleware w umyśle. Zespół musiał znaleźć równowagę, zdecydowanie nie jest to idealne rozwiązanie, ale kompromisy są tutaj wyraźnie po stronie wydajności (zamiast bezpieczeństwa API).

joakim
źródło
1
To jest odpowiedź, która w końcu nabrała dla mnie sensu.
Jimi
2

Powód, dla którego jest to wskaźnik do Request, jest prosty: zmiany w Request przez procedurę obsługi muszą być widoczne dla serwera, więc przekazujemy je tylko przez odwołanie, a nie przez wartość.

Jeśli zagłębisz się w kod biblioteki net / http, przekonasz się, że ResponseWriter jest interfejsem do niewyeksportowanej odpowiedzi struct i przekazujemy strukturę przez odwołanie (przekazujemy wskaźnik do odpowiedzi), a nie przez wartość . ResponseWriter to interfejs, którego program obsługi używa do tworzenia odpowiedzi HTTP. Rzeczywista struktura tworząca kopię zapasową ResponseWriter to niewyeksportowana struktura http.response. Ponieważ nie jest eksportowany, nie możesz go używać bezpośrednio; można go używać tylko za pośrednictwem interfejsu ResponseWriter.

Innymi słowy, oba parametry są przekazywane przez odniesienie; po prostu sygnatura metody przyjmuje ResponseWriter, która jest interfejsem do wskaźnika do struktury, więc wygląda na to, że jest przekazywana przez wartość.

X. Wang
źródło
Ma więcej sensu niż oryginalna odpowiedź dla mnie
Venkata SSKM Chaitanya
Część dotycząca żądania jest rzeczywiście nieprawidłowa, zobacz moją odpowiedź stackoverflow.com/a/56875204/989991
joakim
0

Myślę, że głównym powodem przekazania Requestobiektu jako wskaźnika jest Bodypole. Dla danego żądania HTTP treść możemy odczytać tylko raz. Gdyby Requestobiekt został sklonowany, tak jak by to było, gdyby nie został przekazany jako wskaźnik, mielibyśmy dwa obiekty z różnymi informacjami o tym, ile zostało odczytane z ciała.

md2perpe
źródło