Webserverontwikkeling in Golang - van eenvoudig tot complex

Webserverontwikkeling in Golang - van eenvoudig tot complex

Vijf jaar geleden ben ik begonnen Gophish ontwikkelen, dit bood de mogelijkheid om Golang te leren. Ik besefte dat Go een krachtige taal is, aangevuld met vele bibliotheken. Go is veelzijdig: er kunnen met name probleemloos server-side applicaties mee worden ontwikkeld.

Dit artikel gaat over het schrijven van een server in Go. Laten we beginnen met simpele dingen zoals "Hallo wereld!" en eindigen met een applicatie met de volgende mogelijkheden:

- Let's Encrypt gebruiken voor HTTPS.
— Werken als API-router.
- Werken met middleware.
— Verwerking van statische bestanden.
— Correcte afsluiting.

Skillbox beveelt aan: Praktische cursus "Python-ontwikkelaar vanaf nul".

Herinnering: voor alle lezers van "Habr" - een korting van 10 roebel bij inschrijving voor een Skillbox-cursus met behulp van de promotiecode "Habr".

Hallo Wereld!

In Go maak je heel snel een webserver aan. Hier is een voorbeeld van het gebruik van een handler die de hierboven beloofde "Hallo wereld!" retourneert.

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

Hierna, als u de applicatie uitvoert en de pagina opent localhost, dan zie je meteen de tekst “Hallo wereld!” (als alles goed werkt uiteraard).

We zullen de handler later meerdere keren gebruiken, maar laten we eerst begrijpen hoe alles werkt.

net/http

In het voorbeeld werd het pakket gebruikt net/http, het is het belangrijkste hulpmiddel in Go voor het ontwikkelen van zowel servers als HTTP-clients. Om de code te begrijpen, moeten we de betekenis van drie belangrijke elementen begrijpen: http.Handler, http.ServeMux en http.Server.

HTTP-handlers

Wanneer we een verzoek ontvangen, analyseert de behandelaar dit en genereert een reactie. Handlers in Go zijn als volgt geïmplementeerd:

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

In het eerste voorbeeld wordt de helperfunctie http.HandleFunc gebruikt. Het verpakt een andere functie, die op zijn beurt http.ResponseWriter en http.Request in ServeHTTP overneemt.

Met andere woorden, handlers in Golang worden gepresenteerd in een enkele interface, wat de programmeur veel opties geeft. Middleware wordt bijvoorbeeld geïmplementeerd met behulp van een handler, waarbij ServeHTTP eerst iets doet en vervolgens de ServeHTTP-methode van een andere handler aanroept.

Zoals hierboven vermeld, genereren handlers eenvoudigweg antwoorden op verzoeken. Maar welke specifieke handler moet op een bepaald moment worden gebruikt?

Verzoek om routering

Gebruik een HTTP-multiplexer om de juiste keuze te maken. In een aantal bibliotheken wordt het muxer of router genoemd, maar ze zijn allemaal hetzelfde. De functie van de multiplexer is het analyseren van het verzoekpad en het selecteren van de juiste afhandelingsroutine.

Als u ondersteuning nodig heeft voor complexe routing, is het beter om bibliotheken van derden te gebruiken. Enkele van de meest geavanceerde - gorilla/mux и go-chi/chi, maken deze bibliotheken het mogelijk om zonder problemen tussentijdse verwerking te implementeren. Met hun hulp kunt u wildcardroutering configureren en een aantal andere taken uitvoeren. Hun voordeel is compatibiliteit met standaard HTTP-handlers. Het resultaat is dat u eenvoudige code kunt schrijven die in de toekomst kan worden aangepast.

Het werken met complexe raamwerken in een normale situatie vereist niet-standaardoplossingen, en dit bemoeilijkt het gebruik van standaardhandlers aanzienlijk. Om de overgrote meerderheid van de applicaties te creëren, zal een combinatie van de standaardbibliotheek en een eenvoudige router voldoende zijn.

Queryverwerking

Daarnaast hebben we een component nodig die “luistert” naar inkomende verbindingen en alle verzoeken doorstuurt naar de juiste afhandelaar. http.Server kan deze taak gemakkelijk aan.

Uit het volgende blijkt dat de server verantwoordelijk is voor alle taken die verband houden met de verbindingsverwerking. Dit werkt bijvoorbeeld via het TLS-protocol. Om de http.ListenAndServer-aanroep te implementeren, wordt een standaard HTTP-server gebruikt.

Laten we nu naar meer complexe voorbeelden kijken.

Let's Encrypt toevoegen

Standaard draait onze applicatie via het HTTP-protocol, maar het is aan te raden om het HTTPS-protocol te gebruiken. Dit kan zonder problemen in Go. Als u een certificaat en een privésleutel heeft ontvangen, volstaat het om ListenAndServeTLS te registreren met de juiste certificaat- en sleutelbestanden.

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

Je kunt het altijd beter doen.

Laten we versleutelen biedt gratis certificaten met automatische verlenging. Om gebruik te kunnen maken van de dienst heeft u een pakket nodig autocert.

De eenvoudigste manier om het te configureren is door de autocert.NewListener-methode te gebruiken in combinatie met http.Serve. Met deze methode kunt u TLS-certificaten verkrijgen en bijwerken terwijl de HTTP-server verzoeken verwerkt:

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

Als we openen in de browser example.com, ontvangen we een HTTPS-antwoord "Hallo wereld!"

Als u meer gedetailleerde configuratie nodig heeft, moet u de autocert.Manager-manager gebruiken. Vervolgens maken we onze eigen http.Server-instantie (tot nu toe gebruikten we deze standaard) en voegen we de manager toe aan de 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("", "")

Dit is een eenvoudige manier om volledige HTTPS-ondersteuning te implementeren met automatische certificaatvernieuwing.

Aangepaste routes toevoegen

De standaardrouter in de standaardbibliotheek is goed, maar erg basic. De meeste toepassingen vereisen complexere routering, inclusief geneste routes en jokertekenroutes, of een procedure voor het instellen van padpatronen en parameters.

In dit geval is het de moeite waard om pakketten te gebruiken gorilla/mux и go-chi/chi. Met dit laatste zullen we leren werken - hieronder vindt u een voorbeeld.

Gegeven is het bestand api/v1/api.go met routes voor onze 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
}

We hebben het api/vq-voorvoegsel voor routes in het hoofdbestand ingesteld.

We kunnen dit vervolgens op onze hoofdrouter monteren onder het api/v1/-voorvoegsel in onze hoofdtoepassing:

// 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())

Het gemak waarmee Go met complexe routes kan werken, maakt het mogelijk om de structurering en het onderhoud van grote, complexe applicaties te vereenvoudigen.

Werken met middleware

Staging omvat het omwikkelen van de ene HTTP-handler met de andere, waardoor het mogelijk wordt om snel authenticatie, compressie, loggen en verschillende andere functies uit te voeren.

Laten we als voorbeeld eens kijken naar de http.Handler-interface; we zullen deze gebruiken om een ​​handler te schrijven die servicegebruikers authenticeert.

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

Er zijn routers van derden, zoals chi, waarmee u de middleware-functionaliteit kunt uitbreiden.

Werken met statische bestanden

De Go-standaardbibliotheek bevat mogelijkheden voor het werken met statische inhoud, inclusief afbeeldingen, JavaScript- en CSS-bestanden. Ze zijn toegankelijk via de functie http.FileServer. Het retourneert een handler die bestanden uit een specifieke map bedient.

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

Het is zeker de moeite waard om te onthouden dat http.Dir de inhoud van de map weergeeft als deze niet het hoofdbestand index.html bevat. In dit geval moet u het pakket gebruiken om te voorkomen dat de map wordt gecompromitteerd unindexed.

Correcte afsluiting

Go heeft ook een functie genaamd sierlijk afsluiten van de HTTP-server. Dit kan gedaan worden met behulp van de Shutdown() methode. De server wordt gestart in een goroutine en vervolgens wordt naar het kanaal geluisterd om een ​​interruptsignaal te ontvangen. Zodra het signaal wordt ontvangen, wordt de server uitgeschakeld, maar niet onmiddellijk, maar na enkele seconden.

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 conclusie

Go is een krachtige taal met een vrijwel universele standaardbibliotheek. De standaardmogelijkheden zijn zeer breed en kunnen worden uitgebreid met behulp van interfaces - hierdoor kunt u echt betrouwbare HTTP-servers ontwikkelen.

Skillbox beveelt aan:

Bron: www.habr.com

Voeg een reactie