Webserver Entwécklung zu Golang - vun einfach bis komplex

Webserver Entwécklung zu Golang - vun einfach bis komplex

Virun fënnef Joer hunn ech ugefaang entwéckelen Gophish, dëst huet d'Méiglechkeet Golang ze léieren. Ech hu gemierkt datt Go eng mächteg Sprooch ass, ergänzt vu ville Bibliothéiken. Go ass villsäiteg: besonnesch kann et benotzt ginn fir Server-Säit Uwendungen ouni Probleemer z'entwéckelen.

Dësen Artikel geet iwwer e Server am Go ze schreiwen. Loosst eis mat einfache Saachen ufänken wéi "Hallo Welt!" a mat enger Applikatioun mat de folgende Fäegkeeten ophalen:

- Benotzt Let's Encrypt fir HTTPS.
- Schafft als API Router.
- Schafft mat Middleware.
- Veraarbechtung vu statesche Dateien.
- Korrekt Ausschaltung.

Skillbox recommandéiert: Praktesch Cours "Python Entwéckler vun Null".

Mir erënneren Iech: fir all Habr Lieser - eng Remise vun 10 Rubel wann Dir Iech an all Skillbox Cours aschreift mat dem Habr Promo Code.

Hallo, Welt!

Dir kënnt e Webserver am Go ganz séier erstellen. Hei ass e Beispill fir e Handler ze benotzen deen den "Hallo, Welt!" Versprach uewen zréckginn.

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)
}

Duerno, wann Dir d'Applikatioun lafen an d'Säit opmaachen localhost, da gesitt Dir direkt den Text "Hallo, Welt!" (wann alles richteg funktionéiert, natierlech).

Mir benotzen den Handler méi spéit méi spéit, awer loosst eis als éischt verstoen wéi alles funktionnéiert.

net/http

D'Beispill huet de Package benotzt net/http, et ass dat primärt Tool am Go fir béid Serveren an HTTP Clienten z'entwéckelen. Fir de Code ze verstoen, loosst eis d'Bedeitung vun dräi wichtegen Elementer verstoen: http.Handler, http.ServeMux an http.Server.

HTTP Handler

Wa mir eng Ufro kréien, analyséiert den Handler et a generéiert eng Äntwert. Handler am Go gi wéi follegt ëmgesat:

type Handler interface {
        ServeHTTP(ResponseWriter, *Request)
}

Dat éischt Beispill benotzt d'http.HandleFunc Helper Funktioun. Et wéckelt eng aner Funktioun, déi am Tour http.ResponseWriter an http.Request an ServeHTTP hëlt.

An anere Wierder, Handler an Golang sinn an engem eenzegen Interface presentéiert, déi vill Méiglechkeeten fir de Programméierer gëtt. Also, zum Beispill, gëtt Middleware mat engem Handler implementéiert, wou ServeHTTP als éischt eppes mécht an dann d'ServeHTTP Method vun engem aneren Handler nennt.

Wéi uewen ernimmt, generéieren Handler einfach Äntwerten op Ufroen. Awer wéi ee bestëmmten Handler soll zu engem bestëmmten Zäitpunkt benotzt ginn?

Ufro Routing

Fir déi richteg Wiel ze maachen, benotzt en HTTP-Multiplexer. An enger Zuel vu Bibliothéiken gëtt et muxer oder Router genannt, awer si sinn all déiselwecht Saach. D'Funktioun vum Multiplexer ass den Ufrowee ze analyséieren an de passenden Handler ze wielen.

Wann Dir Ënnerstëtzung fir komplexe Routing braucht, dann ass et besser Drëtt-Partei Bibliothéiken ze benotzen. E puer vun de fortgeschrattsten - gorilla / mux и go-chi/chi, dës Bibliothéike maachen et méiglech, Zwëschenveraarbechtung ouni Probleemer ëmzesetzen. Mat hirer Hëllef kënnt Dir Wildcard Routing konfiguréieren an eng Rei aner Aufgaben ausféieren. Hire Virdeel ass Kompatibilitéit mat Standard HTTP-Handler. Als Resultat kënnt Dir einfache Code schreiwen, deen an Zukunft geännert ka ginn.

Mat komplexe Kaderen an enger normaler Situatioun ze schaffen erfuerdert net-Standardléisungen, an dëst komplizéiert däitlech d'Benotzung vu Standardhanteren. Fir déi grouss Majoritéit vun Uwendungen ze kreéieren, ass eng Kombinatioun vun der Standardbibliothéik an engem einfache Router genuch.

Query Veraarbechtung

Zousätzlech brauche mir e Komponent deen op erakommende Verbindungen "lauschtert" an all Ufro un de richtege Handler redirect. http.Server kann dës Aufgab einfach handhaben.

Déi folgend weist datt de Server verantwortlech ass fir all Aufgaben déi mat der Verbindungsveraarbechtung verbonne sinn. Dëst, zum Beispill, funktionnéiert mam TLS Protokoll. Fir den http.ListenAndServer Uruff ëmzesetzen, gëtt e Standard HTTP-Server benotzt.

Loosst eis elo méi komplex Beispiller kucken.

Füügt Let's Encrypt

Par défaut leeft eis Applikatioun iwwer den HTTP-Protokoll, awer et ass recommandéiert den HTTPS-Protokoll ze benotzen. Dëst kann ouni Probleemer am Go gemaach ginn. Wann Dir e Certificat a private Schlëssel kritt hutt, ass et genuch fir ListenAndServeTLS mat de korrekten Zertifikat a Schlësseldateien unzemellen.

http.ListenAndServeTLS(":443", "cert.pem", "key.pem", nil)

Dir kënnt ëmmer besser maachen.

Loosst eis encryptéieren stellt gratis Certificaten mat automatesch Erneierung. Fir de Service ze benotzen, braucht Dir e Package autocert.

Deen einfachste Wee fir se ze konfiguréieren ass d'Methode autocert.NewListener a Kombinatioun mat http.Serve. D'Methode erlaabt Iech TLS Zertifikater ze kréien an ze aktualiséieren wärend den HTTP Server Ufroen veraarbecht:

http.Serve(autocert.NewListener("example.com"), nil)

Wa mir am Browser opmaachen example.com, mir kréien eng HTTPS Äntwert "Hallo, Welt!"

Wann Dir méi detailléiert Konfiguratioun braucht, da sollt Dir den autocert.Manager Manager benotzen. Da kreéiere mir eis eegen http.Server Instanz (bis elo hu mir se als Standard benotzt) an addéiere de Manager op den TLSConfig Server:

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("", "")

Dëst ass en einfache Wee fir voll HTTPS Ënnerstëtzung mat automatescher Zertifika Erneierung ëmzesetzen.

Dobäizemaachen Mooss routes

De Standardrouter an der Standardbibliothéik abegraff ass gutt, awer et ass ganz Basis. Déi meescht Uwendungen erfuerderen méi komplexe Routing, inklusiv nestéiert a Wildcard routes, oder eng Prozedur fir Weemuster a Parameteren ze setzen.

An dësem Fall ass et derwäert Packagen ze benotzen gorilla / mux и go-chi/chi. Mir léiere wéi mat der leschter Aarbecht ze schaffen - e Beispill gëtt ënnendrënner gewisen.

Gitt ass d'Datei api/v1/api.go mat Strecken fir eis API:

/ HelloResponse is the JSON representation for a customized message
type HelloResponse struct {
Message string `json:"message"`
}
 
// HelloName returns a personalized JSON message
func HelloName(w http.ResponseWriter, r *http.Request) {
name := chi.URLParam(r, "name")
response := HelloResponse{
Message: fmt.Sprintf("Hello %s!", name),
}
jsonResponse(w, response, http.StatusOK)
}
 
// NewRouter returns an HTTP handler that implements the routes for the API
func NewRouter() http.Handler {
r := chi.NewRouter()
r.Get("/{name}", HelloName)
return r
}

Mir setzen den api / vq Präfix fir Strecken an der Haaptdatei.

Mir kënnen dëst dann op eisen Haaptrouter ënner dem Api/v1/ Präfix zréck an eiser Haaptapplikatioun montéieren:

// NewRouter returns a new HTTP handler that implements the main server routes
func NewRouter() http.Handler {
router := chi.NewRouter()
    router.Mount("/api/v1/", v1.NewRouter())
    return router
}
http.Serve(autocert.NewListener("example.com"), NewRouter())

Go's Liichtegkeet mat komplexe Strecken ze schaffen mécht et méiglech d'Strukturéierung an d'Ënnerhalt vu groussen, komplexe Applikatiounen ze vereinfachen.

Schafft mat Middleware

D'Staging beinhalt d'Verpakung vun engem HTTP-Handler mat engem aneren, wat et méiglech mécht séier Authentifikatioun, Kompressioun, Logging a verschidde aner Funktiounen auszeféieren.

Als Beispill kucke mer op den http.Handler-Interface, mir benotze se fir en Handler ze schreiwen deen Service Benotzer authentifizéiert.

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
        }
        // Assuming authentication passed, run the original handler
        next.ServeHTTP(w, r)
    })
}

Et gi Drëtt Partei Router, wéi Chi, déi Iech erlaben d'Mëttelware Funktionalitéit ze verlängeren.

Schafft mat statesche Dateien

D'Go Standardbibliothéik enthält Fäegkeeten fir mat statesche Inhalter ze schaffen, dorënner Biller, JavaScript an CSS Dateien. Si kënnen iwwer d'http.FileServer Funktioun zougänglech sinn. Et gëtt en Handler zréck deen Dateien aus engem spezifesche Verzeechnes servéiert.

func NewRouter() http.Handler {
    router := chi.NewRouter()
    r.Get("/{name}", HelloName)
 
// Настройка раздачи статических файлов
staticPath, _ := filepath.Abs("../../static/")
fs := http.FileServer(http.Dir(staticPath))
    router.Handle("/*", fs)
    
    return r

Et ass definitiv derwäert ze erënneren datt http.Dir den Inhalt vum Verzeechnes weist wann et net d'Haaptdatei index.html enthält. An dësem Fall, fir ze verhënneren datt de Verzeichnis kompromittéiert gëtt, sollt Dir de Package benotzen unindexed.

Korrekt Ausschaltung

Go huet och eng Feature genannt graziéis Shutdown vum HTTP Server. Dëst kann mat der Shutdown () Method gemaach ginn. De Server gëtt an enger Goroutine gestart, an dann gëtt de Kanal gelauschtert fir en Ënnerbriechungssignal ze kréien. Soubal d'Signal kritt ass, schalt de Server aus, awer net direkt, awer no e puer Sekonnen.

handler := server.NewRouter()
srv := &http.Server{
    Handler: handler,
}
 
go func() {
srv.Serve(autocert.NewListener(domains...))
}()
 
// Wait for an interrupt
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c
 
// Attempt a graceful shutdown
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
srv.Shutdown(ctx)

Als Conclusioun

Go ass eng mächteg Sprooch mat enger bal universeller Standardbibliothéik. Seng Standardfäegkeeten si ganz breet, a si kënne mat Interfaces verbessert ginn - dëst erlaabt Iech wierklech zouverlässeg HTTP Serveren z'entwéckelen.

Skillbox recommandéiert:

Source: will.com

Setzt e Commentaire