Razvoj web poslužitelja u Golangu - od jednostavnog do složenog

Razvoj web poslužitelja u Golangu - od jednostavnog do složenog

Prije pet godina počeo sam razviti Gophish, ovo je pružilo priliku za učenje golanga. Shvatio sam da je Go moćan jezik, nadopunjen mnogim bibliotekama. Go je svestran: posebno se može koristiti za razvoj aplikacija na strani poslužitelja bez ikakvih problema.

Ovaj članak govori o pisanju poslužitelja u Go. Počnimo s jednostavnim stvarima poput "Hello world!" i završimo s aplikacijom sa sljedećim mogućnostima:

- Korištenje Let's Encrypt za HTTPS.
— Rad kao API usmjerivač.
— Rad s međuprogramom.
— Obrada statičkih datoteka.
— Ispravno isključivanje.

Skillbox preporučuje: Praktični tečaj "Python programer od nule".

Podsjećamo: za sve čitatelje "Habra" - popust od 10 000 rubalja pri upisu na bilo koji tečaj Skillbox koristeći promotivni kod "Habr".

Pozdrav svijete!

U Gou možete vrlo brzo stvoriti web poslužitelj. Evo primjera korištenja rukovatelja koji vraća gore obećani "Hello, world!".

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

Nakon toga, ako pokrenete aplikaciju i otvorite stranicu localhost, tada ćete odmah vidjeti tekst "Hello, world!" (ako sve radi kako treba, naravno).

Kasnije ćemo više puta koristiti rukovatelj, ali prvo shvatimo kako sve funkcionira.

net/http

U primjeru je korišten paket net/http, to je primarni alat u Gou za razvoj poslužitelja i HTTP klijenata. Da bismo razumjeli kod, shvatimo značenje tri važna elementa: http.Handler, http.ServeMux i http.Server.

HTTP rukovatelji

Kada primimo zahtjev, rukovatelj ga analizira i generira odgovor. Rukovatelji u Go implementirani su na sljedeći način:

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

Prvi primjer koristi pomoćnu funkciju http.HandleFunc. Omota drugu funkciju, koja zauzvrat uzima http.ResponseWriter i http.Request u ServeHTTP.

Drugim riječima, rukovatelji u Golangu predstavljeni su u jednom sučelju, što daje mnogo opcija programeru. Tako se, na primjer, međuprogram implementira pomoću rukovatelja, pri čemu ServeHTTP prvo radi nešto, a zatim poziva metodu ServeHTTP drugog rukovatelja.

Kao što je gore spomenuto, rukovatelji jednostavno generiraju odgovore na zahtjeve. Ali koji bi se određeni rukovatelj trebao koristiti u određenom trenutku?

Usmjeravanje zahtjeva

Da biste napravili pravi izbor, koristite HTTP multiplekser. U velikom broju biblioteka to se naziva muxer ili router, ali sve su to iste stvari. Funkcija multipleksera je analizirati put zahtjeva i odabrati odgovarajući rukovatelj.

Ako vam je potrebna podrška za složeno usmjeravanje, bolje je koristiti biblioteke trećih strana. Neki od najnaprednijih - gorila/muks и go-chi/chi, te biblioteke omogućuju implementaciju srednje obrade bez ikakvih problema. Uz njihovu pomoć možete konfigurirati usmjeravanje pomoću zamjenskih znakova i obavljati niz drugih zadataka. Njihova prednost je kompatibilnost sa standardnim HTTP rukovateljima. Kao rezultat toga, možete napisati jednostavan kod koji se može modificirati u budućnosti.

Rad sa složenim okvirima u normalnoj situaciji zahtijevat će nestandardna rješenja, a to značajno komplicira korištenje zadanih rukovatelja. Za izradu velike većine aplikacija bit će dovoljna kombinacija zadane knjižnice i jednostavnog usmjerivača.

Obrada upita

Osim toga, potrebna nam je komponenta koja će "osluškivati" dolazne veze i preusmjeravati sve zahtjeve ispravnom rukovatelju. http.Server može lako riješiti ovaj zadatak.

Sljedeće pokazuje da je poslužitelj odgovoran za sve zadatke koji se odnose na obradu veze. Ovo, na primjer, radi pomoću TLS protokola. Za implementaciju poziva http.ListenAndServer koristi se standardni HTTP poslužitelj.

Sada pogledajmo složenije primjere.

Dodavanje Let's Encrypt

Prema zadanim postavkama, naša aplikacija radi preko HTTP protokola, ali preporučuje se korištenje HTTPS protokola. To se može učiniti bez problema u Go-u. Ako ste dobili certifikat i privatni ključ, dovoljno je registrirati ListenAndServeTLS s ispravnim datotekama certifikata i ključa.

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

Uvijek može bolje.

Šifrirajmo nudi besplatne certifikate s automatskim obnavljanjem. Za korištenje usluge potreban vam je paket autocert.

Najlakši način za konfiguraciju je korištenje metode autocert.NewListener u kombinaciji s http.Serve. Metoda vam omogućuje dobivanje i ažuriranje TLS certifikata dok HTTP poslužitelj obrađuje zahtjeve:

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

Ako otvorimo u pregledniku example.com, primit ćemo HTTPS odgovor "Hello, world!"

Ako trebate detaljniju konfiguraciju, trebali biste koristiti upravitelja autocert.Manager. Zatim kreiramo vlastitu instancu http.Server (do sada smo je koristili prema zadanim postavkama) i dodamo upravitelja na TLSConfig poslužitelj:

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

Ovo je jednostavan način implementacije pune HTTPS podrške s automatskim obnavljanjem certifikata.

Dodavanje prilagođenih ruta

Zadani usmjerivač uključen u standardnu ​​biblioteku je dobar, ali je vrlo jednostavan. Većina aplikacija zahtijeva složenije usmjeravanje, uključujući ugniježđene rute i rute sa zamjenskim znakovima, ili postupak za postavljanje uzoraka i parametara putanje.

U ovom slučaju vrijedi koristiti pakete gorila/muks и go-chi/chi. Naučit ćemo kako raditi s potonjim - primjer je prikazan u nastavku.

Dana je datoteka api/v1/api.go koja sadrži rute za naš 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
}

Postavili smo api/vq prefiks za rute u glavnoj datoteci.

Zatim to možemo montirati na naš glavni usmjerivač pod prefiksom api/v1/ nazad u našoj glavnoj aplikaciji:

// 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-ova jednostavnost rada sa složenim rutama omogućuje pojednostavljenje strukturiranja i održavanja velikih, složenih aplikacija.

Rad s međuprogramom

Staging uključuje omatanje jednog HTTP rukovatelja drugim, što omogućuje brzo izvođenje provjere autentičnosti, kompresije, zapisivanja i nekoliko drugih funkcija.

Kao primjer, pogledajmo sučelje http.Handler; koristit ćemo ga za pisanje rukovatelja koji autentifikuje korisnike usluge.

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

Postoje usmjerivači treće strane, kao što je chi, koji vam omogućuju proširenje funkcionalnosti međuprograma.

Rad sa statičkim datotekama

Go standardna biblioteka uključuje mogućnosti za rad sa statičnim sadržajem, uključujući slike, JavaScript i CSS datoteke. Može im se pristupiti putem funkcije http.FileServer. Vraća rukovatelj koji poslužuje datoteke iz određenog direktorija.

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

Svakako je vrijedno zapamtiti da http.Dir prikazuje sadržaj direktorija ako ne sadrži glavnu datoteku index.html. U tom slučaju, kako biste spriječili kompromitaciju imenika, trebali biste koristiti paket unindexed.

Ispravno isključivanje

Go također ima značajku koja se zove elegantno isključivanje HTTP poslužitelja. To se može učiniti pomoću metode Shutdown(). Poslužitelj se pokreće u goroutineu, a zatim se kanal osluškuje kako bi primio signal prekida. Čim se primi signal, server se isključuje, ali ne odmah, već nakon nekoliko sekundi.

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)

Kao zaključak

Go je moćan jezik s gotovo univerzalnom standardnom bibliotekom. Njegove zadane mogućnosti su vrlo široke i mogu se poboljšati pomoću sučelja - to vam omogućuje da razvijete istinski pouzdane HTTP poslužitelje.

Skillbox preporučuje:

Izvor: www.habr.com

Dodajte komentar