Warum ist in Go HTTP-Handlern der ResponseWriter ein Wert, der Request ein Zeiger?

82

Ich lerne Go, indem ich eine App für GAE schreibe, und dies ist die Signatur einer Handlerfunktion:

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

Ich bin hier ein Zeiger-Neuling. Warum ist das RequestObjekt ein Zeiger, aber das ResponseWriternicht? Muss es so sein oder ist dies nur, um einen erweiterten zeigerbasierten Code zu ermöglichen?

Sudhir Jonathan
quelle

Antworten:

64

Was Sie bekommen, wist ein Zeiger auf den nicht exportierten Typ, http.responseaber wie ResponseWritereine Schnittstelle ist das nicht sichtbar.

Von server.go :

type ResponseWriter interface {
    ...
}

Auf der anderen Seite rist ein Zeiger auf eine konkrete Struktur, daher die Notwendigkeit, eine Referenz explizit zu übergeben.

Von request.go :

type Request struct {
    ...
}
Denys Séguret
quelle
28

Das http.ResponseWriterist eine Schnittstelle, und die vorhandenen Typen, die diese Schnittstelle implementieren, sind Zeiger. Das bedeutet, dass kein Zeiger auf diese Schnittstelle verwendet werden muss, da dieser bereits von einem Zeiger "unterstützt" wird. Dieses Konzept ist ein wenig von einem des Go Entwicklers startet beschreiben hier zwar eine Art der Umsetzung http.ResponseWriter brauchte keinen Zeiger zu sein, wäre es nicht sinnvoll sein, zumindest nicht innerhalb unterwegs http - Servers.

http.Requestist keine Schnittstelle, sondern nur eine Struktur. Da wir diese Struktur ändern möchten und der Webserver diese Änderungen sehen soll, muss es sich um einen Zeiger handeln. Wenn es nur ein Strukturwert wäre, würden wir nur eine Kopie davon ändern, die der Webserver, der unseren Code aufruft, nicht sehen konnte.

nr
quelle
1
Ich denke nicht, dass das richtig ist. Werte hinter Zeigern können Schnittstellen genauso implementieren wie Werte, sodass hier keine Unterscheidung erforderlich ist. Ein Typ mit dem Basistyp int kann eine Schnittstelle erfüllen, ohne ein Zeiger zu sein oder von einem unterstützt zu werden.
Nemo
2
Nun, ich wollte nicht implizieren, dass alle Dinge, die eine Schnittstelle implementieren, Zeiger sein müssen. So etwas wie ein ResponseWriter, der den Status des Werts hinter der Schnittstelle ändern müsste, um etwas Nützliches zu tun, und der erfordert, dass dieser Wert ein Zeigertyp ist (wie der Blog-Beitrag, auf den ich verlinkt habe, auch sagt). Aber ja, http.ResponseWriter könnte theoretisch von einem int implementiert werden (dessen Methoden diesen int-Wert niemals ändern könnten).
Nr.
Diese Antwort war super hilfreich. Dieser Link ist von unschätzbarem Wert und bietet eine klare Erklärung für diejenigen von uns, die noch keine Erfahrung mit Zeigern haben. "Das heißt, obwohl es keine explizite Markierung gibt, verhalten sich Schnittstellenobjekte oft wie Zeiger. Dies kann verwirrend sein, bis Sie verstehen, was wirklich vor sich geht."
Cody Django
1
Der Teil über Anfrage ist tatsächlich falsch, siehe meine Antwort stackoverflow.com/a/56875204/989991
joakim
6

Wie in vielen anderen Antworten hier und anderswo richtig erwähnt, ResponseWriterhandelt es sich um eine Schnittstelle, deren Auswirkungen in SO- Antworten und Blogs ausführlich beschrieben wurden .

Was ich ansprechen möchte, ist meiner Meinung nach das große - und gefährliche - Missverständnis hier, dass die Grundanforderung als "Referenz" übergeben wird (obwohl so etwas in Go nicht wirklich existiert ), dass "wir Änderungen vornehmen wollen dazu sichtbar für den Server ".

Ein paar Antworten zitieren:

[..] es ist nur eine Struktur, und da wir diese Struktur ändern möchten und der Webserver diese Änderungen sehen soll, muss es ein Zeiger sein [..] SO

[..] Änderungen an Request by the Handler müssen für den Server sichtbar sein, daher übergeben wir sie nur als Referenz anstatt als Wert [..] SO

Das ist falsch ; Tatsächlich warnen die Dokumente ausdrücklich davor, die Anfrage zu manipulieren oder zu mutieren :

Mit Ausnahme des Lesens des Körpers sollten Handler die bereitgestellte Anforderung nicht ändern.

Im Gegenteil, nein? :-)

Wenn Sie die Anforderung ändern möchten, z. B. einen Ablaufverfolgungsheader anhängen, bevor Sie ihn an den nächsten Handler in einer Middleware-Kette weiterleiten, müssen Sie die Anforderung kopieren und die kopierte Version an die Kette weitergeben.

Anfragen um das Verhalten zu ändern Änderungen der eingehenden Anforderung zu ermöglichen , erhoben wurden mit dem Go - Team aber zumindest einig vorhandener Code wahrscheinlich führen würden wie folgt zu ändern unerwartet brechen.

Warum einen Zeiger verwenden, wenn wir den Leuten ausdrücklich sagen, dass sie die Anfrage nicht mutieren sollen? Performance , Requestist eine große Struktur und Kopieren kann Leistung bringen, vor allem bei langen Middleware - Ketten im Auge behalten. Das Team musste ein Gleichgewicht finden, definitiv keine ideale Lösung, aber die Kompromisse sind hier eindeutig auf der Seite der Leistung (anstelle der API-Sicherheit).

Joakim
quelle
Dies ist die Antwort, die mir endlich Sinn machte.
Jimi
1

Der Grund, warum es sich um einen Zeiger auf Request handelt, ist einfach: Änderungen an Request durch den Handler müssen für den Server sichtbar sein, sodass wir sie nur als Referenz und nicht als Wert übergeben.

Wenn Sie sich in den Code der net / http-Bibliothek vertiefen, werden Sie feststellen, dass ResponseWriter eine Schnittstelle zu einer nicht exportierten Strukturantwort ist, und wir übergeben die Struktur als Referenz (wir übergeben einen Zeiger auf die Antwort) und nicht als Wert . ResponseWriter ist eine Schnittstelle, über die ein Handler eine HTTP-Antwort erstellt. Die eigentliche Struktur, die ResponseWriter sichert, ist die nicht exportierte Struktur http.response. Da es nicht exportiert wird, können Sie es nicht direkt verwenden. Sie können es nur über die ResponseWriter-Schnittstelle verwenden.

Mit anderen Worten, beide Parameter werden als Referenz übergeben; Es ist nur so, dass die Methodensignatur einen ResponseWriter verwendet, der eine Schnittstelle zu einem Zeiger auf eine Struktur darstellt. Es sieht also so aus, als ob er als Wert übergeben wird.

X. Wang
quelle
Sinnvoller
Der Teil über Anfrage ist tatsächlich falsch, siehe meine Antwort stackoverflow.com/a/56875204/989991
joakim
0

Ich denke, dass der Hauptgrund für die RequestÜbergabe des Objekts als Zeiger das BodyFeld ist. Für eine bestimmte HTTP-Anfrage kann der Text nur einmal gelesen werden. Wenn das RequestObjekt geklont würde, wie es wäre, wenn es nicht als Zeiger übergeben würde, hätten wir zwei Objekte mit unterschiedlichen Informationen darüber, wie viel aus dem Körper gelesen wurde.

md2perpe
quelle