
Vor fünf Jahren begann ich mit der
Entwicklung von Gophish , was es ermöglichte, Golang zu lernen. Ich erkannte, dass Go eine mächtige Sprache ist, deren Fähigkeiten durch viele Bibliotheken ergänzt werden. Go ist universell: Insbesondere mit seiner Hilfe ist es möglich, Serveranwendungen ohne Probleme zu entwickeln.
In diesem Artikel geht es um das Schreiben eines Servers in Go. Beginnen wir mit einfachen Dingen wie „Hallo Welt!“ Und enden mit einer Anwendung mit den folgenden Funktionen:
- Verwenden von Let's Encrypt für HTTPS.
- Arbeiten Sie als API-Router.
- Arbeiten Sie mit Middleware.
- Verarbeitung statischer Dateien.
- Richtiges Herunterfahren.
Skillbox empfiehlt: Der Python-Entwickler von Grund auf zum Anfassen .
Wir erinnern Sie daran: Für alle Leser von „Habr“ - ein Rabatt von 10.000 Rubel bei der Anmeldung für einen Skillbox-Kurs mit dem Promo-Code „Habr“.
Hallo Welt!
Das Erstellen eines Webservers unter Go ist sehr schnell. Hier ist ein Beispiel für die Verwendung eines Handlers, der das oben versprochene "Hallo Welt!" Zurückgibt.
package main import ( "fmt" "net/http" ) func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!") }) http.ListenAndServe(":80", nil) }
Wenn Sie danach die Anwendung starten und die
localhost- Seite öffnen, wird sofort der Text "Hallo Welt!" Angezeigt. (Natürlich, wenn alles richtig funktioniert).
Als nächstes werden wir den Handler wiederholt verwenden, aber zuerst wollen wir verstehen, wie alles funktioniert.
net / http
In diesem Beispiel wurde das Paket
net/http
, das das Hauptwerkzeug in Go für die Entwicklung von Servern und HTTP-Clients ist. Um den Code zu verstehen, schauen wir uns die Bedeutung von drei wichtigen Elementen an: http.Handler, http.ServeMux und http.Server.
HTTP-Handler
Wenn wir eine Anfrage erhalten, analysiert der Handler diese und bildet eine Antwort. Go-Handler werden wie folgt implementiert:
type Handler interface { ServeHTTP(ResponseWriter, *Request) }
Das erste Beispiel verwendet die Hilfsfunktion http.HandleFunc. Es umschließt eine andere Funktion, die wiederum http.ResponseWriter und http.Request in ServeHTTP akzeptiert.
Mit anderen Worten, Golang-Handler werden durch eine einzige Schnittstelle dargestellt, die dem Programmierer viele Möglichkeiten bietet. So wird beispielsweise Middleware mithilfe eines Handlers implementiert, wobei ServeHTTP zuerst etwas ausführt und dann die ServeHTTP-Methode eines anderen Handlers aufruft.
Wie oben erwähnt, generieren Handler einfach Antworten auf Anforderungen. Aber welcher bestimmte Handler sollte zu einem bestimmten Zeitpunkt verwendet werden?
Routing anfordern
Verwenden Sie den HTTP-Multiplexer, um die richtige Wahl zu treffen. In einigen Bibliotheken wird es als Muxer oder Router bezeichnet, aber es ist alles das Gleiche. Die Multiplexerfunktion besteht darin, den Anforderungspfad zu analysieren und den geeigneten Handler auszuwählen.
Wenn Sie Unterstützung für komplexes Routing benötigen, ist es besser, Bibliotheken von Drittanbietern zu verwenden. Eine der fortschrittlichsten ist
Gorilla / Mux und
Go-Chi / Chi . Diese Bibliotheken ermöglichen die problemlose Implementierung der Zwischenverarbeitung. Mit ihrer Hilfe können Sie das Wildcard-Routing konfigurieren und eine Reihe anderer Aufgaben ausführen. Ihr Plus ist die Kompatibilität mit Standard-HTTP-Handlern. Infolgedessen können Sie einfachen Code mit der Möglichkeit seiner zukünftigen Änderung schreiben.
Das Arbeiten mit komplexen Frameworks in einer normalen Situation erfordert nicht ganz Standardlösungen, was die Verwendung von Standardhandlern erheblich erschwert. Um die überwiegende Mehrheit der Anwendungen zu erstellen, reicht eine Kombination aus der Standardbibliothek und einem einfachen Router aus.
Anfrage bearbeiten
Darüber hinaus benötigen wir eine Komponente, die eingehende Verbindungen „abhört“ und alle Anforderungen an den richtigen Handler umleitet. Diese Aufgabe kann problemlos von http.Server ausgeführt werden.
Das Folgende zeigt, dass der Server für alle Aufgaben verantwortlich ist, die sich auf die Verbindungsverarbeitung beziehen. Dies funktioniert beispielsweise mit dem TLS-Protokoll. Zum Implementieren des Aufrufs von http.ListenAndServer wird ein Standard-HTTP-Server verwendet.
Schauen wir uns nun komplexere Beispiele an.
Hinzufügen von Let's Encrypt
Standardmäßig wird unsere Anwendung über das HTTP-Protokoll ausgeführt. Es wird jedoch empfohlen, das HTTPS-Protokoll zu verwenden. In Go ist dies problemlos möglich. Wenn Sie ein Zertifikat und einen privaten Schlüssel erhalten haben, registrieren Sie ListenAndServeTLS einfach mit den richtigen Zertifikat- und Schlüsseldateien.
http.ListenAndServeTLS(":443", "cert.pem", "key.pem", nil)
Sie können es immer besser machen.
Let's Encrypt bietet kostenlose Zertifikate mit der Möglichkeit zur automatischen Verlängerung. Um den Dienst nutzen zu können, benötigen Sie das
autocert
Paket.
Die einfachste Möglichkeit zur Konfiguration besteht darin, die Methode autocert.NewListener in Kombination mit http.Serve zu verwenden. Mit dieser Methode können Sie TLS-Zertifikate empfangen und erneuern, während der HTTP-Server Anforderungen verarbeitet:
http.Serve(autocert.NewListener("example.com"), nil)
Wenn wir
example.com im Browser öffnen, erhalten wir die HTTPS-Antwort "Hallo Welt!".
Wenn Sie eine gründlichere Konfiguration benötigen, sollten Sie den Manager autocert.Manager verwenden. Dann erstellen wir unsere eigene http.Server-Instanz (bis jetzt haben wir sie standardmäßig verwendet) und fügen den Manager dem TLSConfig-Server hinzu:
m := &autocert.Manager{ Cache: autocert.DirCache("golang-autocert"), Prompt: autocert.AcceptTOS, HostPolicy: autocert.HostWhitelist("example.org", "www.example.org"), } server := &http.Server{ Addr: ":443", TLSConfig: m.TLSConfig(), } server.ListenAndServeTLS("", "")
Dies ist eine einfache Möglichkeit, die vollständige HTTPS-Unterstützung mit automatischer Zertifikatserneuerung zu implementieren.
Benutzerdefinierte Routen hinzufügen
Der in der Standardbibliothek enthaltene Standardrouter ist gut, aber sehr einfach. Die meisten Anwendungen erfordern ein komplexeres Routing, einschließlich verschachtelter Routen und Platzhalterrouten, oder das Verfahren zum Festlegen von Mustern und Pfadparametern.
In diesem Fall sollten Sie die Pakete
Gorilla / Mux und
Go-Chi / Chi verwenden . Wir werden lernen, wie man mit letzterem arbeitet - ein Beispiel ist unten gezeigt.
Gegeben ist die Datei api / v1 / api.go, die die Routen für unsere API enthält:
/ HelloResponse is the JSON representation for a customized message type HelloResponse struct { Message string `json:"message"` }
Wir setzen das Präfix api / vq für Routen in der Hauptdatei.
Wir können dies dann auf unserem Hauptrouter unter dem Präfix api / v1 / in unserer Hauptanwendung mounten:
Die Einfachheit der Arbeit mit komplexen Routen in Go ermöglicht es, die Strukturierung durch die Bereitstellung großer komplexer Anwendungen zu vereinfachen.
Arbeiten Sie mit Middleware
Bei der Zwischenverarbeitung wird ein HTTP-Handler mit einem anderen umbrochen, wodurch eine schnelle Authentifizierung, Komprimierung, Aufzeichnung und einige andere Funktionen möglich sind.
Betrachten Sie als Beispiel die Schnittstelle http.Handler, mit deren Hilfe wir einen Handler mit Authentifizierung der Benutzer des Dienstes schreiben.
func RequireAuthentication(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if !isAuthenticated(r) { http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) return }
Es gibt Router von Drittanbietern, z. B. Chi, mit denen Sie die Funktionalität der Zwischenverarbeitung erweitern können.
Arbeiten Sie mit statischen Dateien
Die Go-Standardbibliothek enthält Funktionen zum Arbeiten mit statischen Inhalten, einschließlich Bildern, sowie JavaScript- und CSS-Dateien. Sie können über die Funktion http.FileServer aufgerufen werden. Es gibt einen Handler zurück, der Dateien aus einem bestimmten Verzeichnis verteilt.
func NewRouter() http.Handler { router := chi.NewRouter() r.Get("/{name}", HelloName)
Beachten Sie, dass http.Dir den Inhalt des Verzeichnisses anzeigt, wenn es nicht über die Hauptdatei index.html verfügt. In diesem Fall lohnt es sich, das nicht
unindexed
Paket zu verwenden, um zu verhindern, dass das Verzeichnis kompromittiert wird.
Richtiges Herunterfahren
Go hat auch eine Funktion wie das korrekte Herunterfahren des HTTP-Servers. Dies kann mit der Shutdown () -Methode erfolgen. Der Server startet in Goroutine und dann wird der Kanal abgehört, um ein Interrupt-Signal zu empfangen. Sobald das Signal empfangen wird, wird der Server heruntergefahren, jedoch nicht sofort, sondern nach einigen Sekunden.
handler := server.NewRouter() srv := &http.Server{ Handler: handler, } go func() { srv.Serve(autocert.NewListener(domains...)) }()
Abschließend
Go ist eine leistungsstarke Sprache mit einer fast universellen Standardbibliothek. Die Standardfunktionen sind sehr umfangreich und können mithilfe von Schnittstellen verstärkt werden. Auf diese Weise können Sie wirklich zuverlässige HTTP-Server entwickeln.
Skillbox empfiehlt: