Ontwikkeling van webbedieners in Golang – van eenvoudig tot kompleks

Ontwikkeling van webbedieners in Golang – van eenvoudig tot kompleks

Vyf jaar gelede het ek begin ontwikkel Gophish, dit het dit moontlik gemaak om Golang te leer. Ek het besef dat Go 'n kragtige taal is, wat deur baie biblioteke aangevul word. Go is veelsydig: veral, jy kan maklik bedienerkant-toepassings daarmee ontwikkel.

Hierdie artikel handel oor die skryf van 'n bediener in Go. Kom ons begin met eenvoudige dinge soos "Hallo wêreld!" en eindig met 'n toepassing met hierdie kenmerke:

- Gebruik Let's Encrypt vir HTTPS.
- Werk as 'n API-roeteerder.
- Werk met middelware.
- Hantering van statiese lêers.
- Korrekte afsluiting.

Skillbox beveel aan: Praktiese kursus "Python-ontwikkelaar van nuuts af".

Ons herinner: vir alle lesers van "Habr" - 'n afslag van 10 000 roebels wanneer u inskryf vir enige Skillbox-kursus met behulp van die "Habr"-promosiekode.

Hallo, wêreld!

Die skep van 'n webbediener in Go kan baie vinnig wees. Hier is 'n voorbeeld van die gebruik van 'n hanteerder wat die "Hallo, wêreld!" gee wat hierbo belowe is.

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

Daarna, as jy die toepassing hardloop en die bladsy oopmaak localhost, dan sal jy dadelik die teks sien "Hallo, wêreld!" (natuurlik, as alles reg werk).

Ons sal die hanteerder herhaaldelik in die volgende gebruik, maar laat ons eers verstaan ​​hoe dit alles werk.

net/http

Die voorbeeld het die pakket gebruik net/http, is Go se primêre hulpmiddel vir die ontwikkeling van beide bedieners en HTTP-kliënte. Om die kode te verstaan, kom ons verstaan ​​die betekenis van drie belangrike elemente: http.Handler, http.ServeMux en http.Server.

HTTP-hanteerders

Wanneer ons 'n versoek ontvang, ontleed die hanteerder dit en genereer 'n antwoord. Hanteerders in Go word soos volg geïmplementeer:

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

Die eerste voorbeeld gebruik die http.HandleFunc-helperfunksie. Dit omvou 'n ander funksie wat op sy beurt http.ResponseWriter en http.Request in ServeHTTP aanvaar.

Met ander woorde, hanteerders in Golang word verteenwoordig deur 'n enkele koppelvlak, wat baie geleenthede vir die programmeerder bied. So, byvoorbeeld, middelware word geïmplementeer met behulp van 'n hanteerder, waar ServeHTTP eers iets doen en dan die ServeHTTP-metode van 'n ander hanteerder oproep.

Soos hierbo genoem, vorm hanteerders bloot antwoorde op versoeke. Maar watter hanteerder moet op 'n spesifieke tyd gebruik word?

Versoek roetering

Om die regte keuse te maak, gebruik die HTTP-multiplekser. In 'n aantal biblioteke word dit muxer of router genoem, maar hulle is almal dieselfde. Die funksie van die multiplekser is om die versoekpad te ontleed en die toepaslike hanteerder te kies.

As u ondersteuning benodig vir komplekse roetering, is dit beter om derdeparty-biblioteke te gebruik. Een van die mees gevorderde gorilla/mux и go-chi/chi, maak hierdie biblioteke dit moontlik om intermediêre verwerking sonder enige probleme te implementeer. Met hul hulp kan u wildkaartroetering opstel en 'n aantal ander take verrig. Hul voordeel is verenigbaarheid met standaard HTTP-hanteerders. As gevolg hiervan kan u eenvoudige kode skryf wat in die toekoms gewysig kan word.

Werk met komplekse raamwerke in 'n normale situasie sal nie heeltemal standaard oplossings vereis nie, en dit bemoeilik die gebruik van verstekhanteerders grootliks. Die kombinasie van die verstekbiblioteek en 'n eenvoudige router sal voldoende wees om die oorgrote meerderheid toepassings te skep.

Navraagverwerking

Daarbenewens benodig ons 'n komponent wat sal "luister" vir inkomende verbindings en alle versoeke na die korrekte hanteerder herlei. http.Server kan hierdie taak maklik hanteer.

Die volgende wys dat die bediener verantwoordelik is vir alle take wat verband hou met die hantering van verbindings. Dit is byvoorbeeld werk op die TLS-protokol. 'n Standaard HTTP-bediener word gebruik om die http.ListenAndServer-oproep te implementeer.

Kom ons kyk nou na meer komplekse voorbeelde.

Voeg Let's Encrypt by

By verstek loop ons toepassing oor die HTTP-protokol, maar dit word aanbeveel om die HTTPS-protokol te gebruik. In Go kan dit sonder probleme gedoen word. As jy 'n sertifikaat en 'n private sleutel ontvang het, is dit genoeg om ListenAndServeTLS met die korrekte sertifikaat en sleutellêers te skryf.

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

Jy kan altyd beter doen.

Kom ons enkripteer gee gratis sertifikate met die moontlikheid van outomatiese hernuwing. Om die diens te gebruik, benodig jy 'n pakket autocert.

Die maklikste manier om dit op te stel is om die autocert.NewListener-metode in kombinasie met http.Serve te gebruik. Die metode laat jou toe om TLS-sertifikate te ontvang en te hernu terwyl die HTTP-bediener versoeke verwerk:

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

As ons in die blaaier oopmaak example.com, kry ons 'n HTTPS-reaksie "Hallo, wêreld!".

As jy meer gedetailleerde konfigurasie benodig, moet jy die autocert.Manager gebruik. Dan skep ons ons eie http.Server-instansie (tot dusver het ons dit by verstek gebruik) en voeg die bestuurder by die TLSConfig-bediener:

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 'n maklike manier om volledige HTTPS-ondersteuning te implementeer met outomatiese sertifikaathernuwing.

Voeg pasgemaakte roetes by

Die verstekroeteerder wat by die standaardbiblioteek ingesluit is, is lekker, maar baie basies. Die meeste toepassings benodig meer komplekse roetes, insluitend geneste en jokertekenroetes, of stelpatrone en padparameters.

In hierdie geval moet jy pakkette gebruik gorilla/mux и go-chi/chi. Ons sal leer hoe om met laasgenoemde te werk - 'n voorbeeld word hieronder getoon.

Gegee is die api/v1/api.go-lêer wat die roetes vir ons API bevat:

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

Ons stel die api/vq-voorvoegsel vir die roetes in die hooflêer.

Ons kan dit dan op ons hoofroeteerder monteer onder die api/v1/-voorvoegsel terug in ons hooftoepassing:

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

Die gemak van werk met komplekse roetes in Go maak dit moontlik om die strukturering en instandhouding van groot komplekse toepassings te vereenvoudig.

Werk met middelware

In die geval van intermediêre verwerking, word die vou van een HTTP-hanteerder met 'n ander gebruik, wat dit moontlik maak om vinnig verifikasie, kompressie, logboek en 'n paar ander funksies uit te voer.

As 'n voorbeeld, kom ons kyk na die http.Handler-koppelvlak, met sy hulp sal ons 'n hanteerder skryf met die verifikasie van diensgebruikers.

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

Daar is derdeparty-roeteerders, soos chi, wat jou toelaat om die funksionaliteit van intermediêre verwerking uit te brei.

Werk met statiese lêers

Go se standaardbiblioteek bevat fasiliteite om met statiese inhoud te werk, insluitend beelde, sowel as JavaScript- en CSS-lêers. Hulle kan verkry word deur die http.FileServer-funksie. Dit gee 'n hanteerder terug wat lêers uit 'n spesifieke gids versprei.

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

Maak seker om te onthou dat http.Dir die inhoud van die gids vertoon as dit nie die hoof index.html lêer bevat nie. In hierdie geval, om te verhoed dat die gids kompromitteer word, moet jy die pakket gebruik unindexed.

Korrekte afskakeling

Go het ook so 'n kenmerk soos 'n grasieuse afsluiting van die HTTP-bediener. Dit kan gedoen word met behulp van die Shutdown() metode. Die bediener word in 'n goroutine begin, en dan word na die kanaal geluister om 'n onderbrekingsein te ontvang. Sodra die sein ontvang word, skakel die bediener af, maar nie onmiddellik nie, maar na 'n paar sekondes.

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)

As 'n gevolgtrekking

Go is 'n kragtige taal met 'n byna universele standaardbiblioteek. Die verstekvermoëns daarvan is baie wyd, en jy kan dit versterk met behulp van koppelvlakke - dit laat jou toe om werklik betroubare HTTP-bedieners te ontwikkel.

Skillbox beveel aan:

Bron: will.com

Voeg 'n opmerking