Webserverûntwikkeling yn Golang - fan ienfâldich oant kompleks

Webserverûntwikkeling yn Golang - fan ienfâldich oant kompleks

Fiif jier lyn bin ik begûn ûntwikkeljen Gophish, dit joech in kâns om Golang te learen. Ik realisearre dat Go in krêftige taal is, oanfolle troch in protte biblioteken. Go is alsidich: it kin benammen brûkt wurde foar it ûntwikkeljen fan serverside-applikaasjes sûnder problemen.

Dit artikel giet oer it skriuwen fan in server yn Go. Litte wy begjinne mei ienfâldige dingen lykas "Hallo wrâld!" en einigje mei in applikaasje mei de folgjende mooglikheden:

- Brûk Let's Encrypt foar HTTPS.
- Wurkje as in API-router.
- Wurkje mei middleware.
- Ferwurkjen fan statyske bestannen.
- Korrekte ôfsluting.

Skillbox advisearret: Praktyske kursus "Python-ûntwikkelder fanôf it begjin".

Wy herinnerje: foar alle lêzers fan "Habr" - in koarting fan 10 roebel by it ynskriuwen fan in Skillbox-kursus mei de promoasjekoade "Habr".

Hallo wrâld!

Jo kinne heul fluch in webserver oanmeitsje yn Go. Hjir is in foarbyld fan it brûken fan in handler dy't de hjirboppe taseine "Hallo, wrâld!" werombringt.

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

Nei dit, as jo de applikaasje útfiere en de side iepenje localhost, dan sille jo fuortendaliks de tekst "Hallo, wrâld!" (as alles goed wurket, fansels).

Wy sille letter de handler meardere kearen brûke, mar litte wy earst begripe hoe't alles wurket.

net/http

It foarbyld brûkte it pakket net/http, it is it primêre ark yn Go foar it ûntwikkeljen fan sawol servers as HTTP-kliïnten. Om de koade te begripen, litte wy de betsjutting fan trije wichtige eleminten begripe: http.Handler, http.ServeMux en http.Server.

HTTP-hannelers

As wy in fersyk ûntfange, analysearret de handler it en generearret in antwurd. Handlers yn Go wurde as folget ymplementearre:

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

It earste foarbyld brûkt de helperfunksje http.HandleFunc. It wraps in oare funksje, dy't op syn beurt http.ResponseWriter en http.Request yn ServeHTTP nimt.

Mei oare wurden, handlers yn Golang wurde presintearre yn in inkele ynterface, dat jout in protte opsjes foar de programmeur. Sa, bygelyks, middleware wurdt ymplementearre mei help fan in handler, dêr't ServeHTTP earst docht wat en dan neamt de ServeHTTP metoade fan in oare handler.

Lykas hjirboppe neamd, generearje handlers gewoan antwurden op oanfragen. Mar hokker bepaalde handler moat op in bepaald momint brûkt wurde?

Fersyk Routing

Om de juste kar te meitsjen, brûk in HTTP-multiplexer. Yn in oantal biblioteken hjit it muxer of router, mar se binne allegear itselde ding. De funksje fan 'e multiplexer is om it fersykpaad te analysearjen en de passende handler te selektearjen.

As jo ​​​​stipe nedich hawwe foar komplekse routing, dan is it better om bibleteken fan tredden te brûken. Guon fan 'e meast avansearre - gorilla / mux и go-chi/chi, dizze biblioteken meitsje it mooglik om sûnder problemen tuskenferwurking út te fieren. Mei har help kinne jo wildcard-routing ynstelle en in oantal oare taken útfiere. Har foardiel is kompatibiliteit mei standert HTTP-hannelers. As resultaat kinne jo ienfâldige koade skriuwe dy't yn 'e takomst kin wurde wizige.

Wurkje mei komplekse kaders yn in normale situaasje sil net-standert oplossings fereaskje, en dit komplisearret it gebrûk fan standerthannelers signifikant. Om de grutte mearderheid fan applikaasjes te meitsjen, sil in kombinaasje fan 'e standertbibleteek en in ienfâldige router genôch wêze.

Query ferwurking

Dêrneist hawwe wy in komponint nedich dat sil "harkje" foar ynkommende ferbiningen en alle fersiken omliede nei de juste handler. http.Tsjinner kin dizze taak maklik behannelje.

It folgjende lit sjen dat de tsjinner ferantwurdlik is foar alle taken dy't relatearre binne oan ferbiningsferwurking. Dit wurket bygelyks mei it TLS-protokol. Om de http.ListenAndServer-oprop út te fieren, wurdt in standert HTTP-tsjinner brûkt.

Litte wy no nei mear komplekse foarbylden sjen.

Let's Encrypt tafoegje

Standert rint ús applikaasje oer it HTTP-protokol, mar it wurdt oanrikkemandearre om it HTTPS-protokol te brûken. Dit kin sûnder problemen dien wurde yn Go. As jo ​​in sertifikaat en privee kaai hawwe krigen, dan is it genôch om ListenAndServeTLS te registrearjen mei de juste sertifikaat en kaaibestannen.

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

Jo kinne altyd better dwaan.

Let's Encrypt jout fergese sertifikaten mei automatyske fernijing. Om de tsjinst te brûken, hawwe jo in pakket nedich autocert.

De maklikste manier om it te konfigurearjen is troch de metoade autocert.NewListener te brûken yn kombinaasje mei http.Serve. De metoade lit jo TLS-sertifikaten krije en bywurkje wylst de HTTP-tsjinner fersiken ferwurket:

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

As wy iepenje yn 'e browser example.com, krije wy in HTTPS-antwurd "Hallo, wrâld!"

As jo ​​mear detaillearre konfiguraasje nedich binne, dan moatte jo de autocert.Manager manager brûke. Dan meitsje wy ús eigen http.Server-eksimplaar (oant no hawwe wy it standert brûkt) en foegje de manager ta oan de TLSConfig-tsjinner:

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 in maklike manier om folsleine HTTPS-stipe te ymplementearjen mei automatyske sertifikaatfernijing.

Oanpaste rûtes tafoegje

De standert router opnaam yn de standert bibleteek is goed, mar it is hiel basis. De measte applikaasjes fereaskje mear komplekse routing, ynklusyf nested en wildcard rûtes, of in proseduere foar it ynstellen fan paad patroanen en parameters.

Yn dit gefal is it wurdich om pakketten te brûken gorilla / mux и go-chi/chi. Wy sille leare hoe't jo mei de lêste wurkje kinne - in foarbyld wurdt hjirûnder werjûn.

Opjûn is it bestân api/v1/api.go mei rûtes foar ús 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
}

Wy sette it foarheaksel api / vq foar rûtes yn it haadbestân.

Wy kinne dit dan montearje op ús haadrouter ûnder it foarheaksel api/v1/ werom yn ús haadapplikaasje:

// 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 gemak fan wurkjen mei komplekse rûtes makket it mooglik om de strukturearring en ûnderhâld fan grutte, komplekse applikaasjes te ferienfâldigjen.

Wurkje mei middleware

Staging omfettet it ynpakken fan ien HTTP-handler mei in oare, wêrtroch it mooglik is om fluch ferifikaasje, kompresje, logging en ferskate oare funksjes út te fieren.

Litte wy as foarbyld nei de http.Handler-ynterface sjen; wy sille it brûke om in handler te skriuwen dy't tsjinstbrûkers autentisearret.

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

D'r binne routers fan tredden, lykas chi, wêrtroch jo de funksjonaliteit fan 'e middleware kinne útwreidzje.

Wurkje mei statyske triemmen

De standertbibleteek fan Go omfettet mooglikheden foar wurkjen mei statyske ynhâld, ynklusyf ôfbyldings, JavaScript en CSS-bestannen. Se kinne tagonklik wurde fia de http.FileServer-funksje. It jout in handler dy't tsjinnet triemmen út in spesifike map.

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

It is perfoarst de muoite wurdich om te ûnthâlden dat http.Dir de ynhâld fan 'e map toant as it net de haad index.html-bestân befettet. Yn dit gefal, om foar te kommen dat de map kompromittearre wurdt, moatte jo it pakket brûke unindexed.

Korrekte ôfsluting

Go hat ek in funksje neamd sierlike shutdown fan 'e HTTP-tsjinner. Dit kin dien wurde mei de metoade Shutdown (). De tsjinner wurdt begon yn in goroutine, en dan wurdt it kanaal harke om in ûnderbrekkingssinjaal te ûntfangen. Sadree't it sinjaal ûntfongen is, wurdt de tsjinner útskeakele, mar net fuortendaliks, mar nei in pear sekonden.

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 konklúzje

Go is in krêftige taal mei in hast universele standertbibleteek. De standertmooglikheden binne heul breed, en se kinne wurde ferbettere mei ynterfaces - dit kinne jo wirklik betroubere HTTP-tsjinners ûntwikkelje.

Skillbox advisearret:

Boarne: www.habr.com

Add a comment