Razvoj web servera u Golangu - od jednostavnog do složenog

Razvoj web servera u Golangu - od jednostavnog do složenog

Prije pet godina sam počeo razviti Gophish, omogućilo je učenje Golanga. Shvatio sam da je Go moćan jezik, koji je upotpunjen mnogim bibliotekama. Go je svestran: posebno, pomoću njega možete lako razviti aplikacije na strani servera.

Ovaj članak govori o pisanju servera u Go. Počnimo s jednostavnim stvarima kao što je "Hello world!" i završimo s aplikacijom sa ovim funkcijama:

- Korištenje Let's Encrypt za HTTPS.
- Radi kao API ruter.
- Rad sa srednjim softverom.
- Rukovanje statičkim fajlovima.
- Ispravno gašenje.

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

Podsećamo: za sve čitaoce "Habra" - popust od 10 rubalja pri upisu na bilo koji Skillbox kurs koristeći "Habr" promotivni kod.

Zdravo svijete!

Kreiranje web servera u Go može biti vrlo brzo. Evo primjera korištenja rukovatelja koji vraća "Hello, world!" obećano iznad.

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 "Zdravo, svijete!" (naravno, ako sve radi kako treba).

U nastavku ćemo više puta koristiti rukovalac, ali prvo da shvatimo kako sve to funkcionira.

net/http

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

HTTP Handleri

Kada primimo zahtjev, rukovalac ga analizira i generira odgovor. Obrađivači u Go-u su implementirani na sljedeći način:

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

Prvi primjer koristi pomoćnu funkciju http.HandleFunc. Umotava drugu funkciju koja zauzvrat prihvata http.ResponseWriter i http.Request u ServeHTTP.

Drugim riječima, rukovatelji u Golangu su predstavljeni jednim sučeljem, što daje mnogo mogućnosti programeru. Tako se, na primjer, međuverzija implementira pomoću rukovaoca, gdje ServeHTTP prvo radi nešto, a zatim poziva ServeHTTP metod drugog rukovatelja.

Kao što je gore spomenuto, rukovaoci jednostavno formiraju odgovore na zahtjeve. Ali koji rukovalac treba koristiti u određenom trenutku?

Request Routing

Da biste napravili pravi izbor, koristite HTTP multiplekser. U brojnim bibliotekama naziva se mukser ili ruter, ali svi su isti. Funkcija multipleksera je da analizira putanju zahtjeva i odabere odgovarajući rukovalac.

Ako vam je potrebna podrška za složeno usmjeravanje, onda je bolje koristiti biblioteke trećih strana. Jedan od najnaprednijih gorilla/mux и go-chi/chi, ove biblioteke omogućavaju implementaciju međuobrade bez ikakvih problema. Uz njihovu pomoć možete postaviti džoker rutiranje i obavljati niz drugih zadataka. Njihova prednost je kompatibilnost sa standardnim HTTP rukovaocima. Kao rezultat, možete napisati jednostavan kod koji se može mijenjati u budućnosti.

Rad sa složenim okvirima u normalnoj situaciji zahtijevat će ne baš standardna rješenja, a to uvelike otežava korištenje zadanih rukovatelja. Kombinacija podrazumevane biblioteke i jednostavnog rutera biće dovoljna za kreiranje velike većine aplikacija.

Obrada upita

Osim toga, potrebna nam je komponenta koja će "slušati" dolazne veze i preusmjeravati sve zahtjeve na ispravan rukovalac. http.Server se lako nosi sa ovim zadatkom.

Sljedeće pokazuje da je server odgovoran za sve zadatke koji se odnose na rukovanje vezama. Ovo je, na primjer, rad na TLS protokolu. Standardni HTTP server se koristi za implementaciju poziva http.ListenAndServer.

Pogledajmo sada složenije primjere.

Dodavanje Let's Encrypt

Naša aplikacija podrazumevano radi preko HTTP protokola, ali se preporučuje korišćenje HTTPS protokola. U Go-u to se može uraditi bez problema. Ako ste dobili certifikat i privatni ključ, dovoljno je da napišete ListenAndServeTLS sa ispravnim datotekama certifikata i ključeva.

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

Uvijek možeš bolje.

Hajde da šifriramo daje besplatne sertifikate sa mogućnošću automatskog obnavljanja. Da biste koristili uslugu, potreban vam je paket autocert.

Najlakši način za podešavanje je korištenje autocert.NewListener metode u kombinaciji sa http.Serve. Metoda vam omogućava da primate i obnavljate TLS certifikate dok HTTP server obrađuje zahtjeve:

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

Ako otvorimo u pretraživaču example.com, dobijamo HTTPS odgovor "Zdravo, svijete!".

Ako vam je potrebna detaljnija konfiguracija, trebali biste koristiti autocert.Manager. Zatim kreiramo našu vlastitu http.Server instancu (do sada smo je koristili po defaultu) i dodajemo upravitelja na 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("", "")

Ovo je jednostavan način za implementaciju pune HTTPS podrške uz automatsku obnovu certifikata.

Dodavanje prilagođenih ruta

Zadani ruter uključen u standardnu ​​biblioteku je lijep, ali vrlo jednostavan. Većina aplikacija zahtijeva složenije rutiranje, uključujući ugniježđene i zamjenske rute, ili proceduru za postavljanje obrazaca putanja i parametara.

U ovom slučaju, trebali biste koristiti pakete gorilla/mux и go-chi/chi. Naučit ćemo kako raditi s potonjim - primjer je prikazan u nastavku.

Dat je api/v1/api.go fajl koji 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 ovo možemo montirati na naš glavni ruter pod api/v1/ prefiksom natrag 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())

Lakoća rada sa složenim rutama u Go-u omogućava pojednostavljenje strukturiranja i održavanja velikih složenih aplikacija.

Rad sa srednjim softverom

U slučaju posredne obrade koristi se omotavanje jednog HTTP rukovaoca drugim, što omogućava brzo izvođenje autentifikacije, kompresije, evidentiranja i nekih drugih funkcija.

Kao primjer, razmotrimo sučelje http.Handler, uz njegovu pomoć ćemo napisati rukovalac sa autentifikacijom korisnika 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 ruteri trećih strana, kao što je chi, koji vam omogućavaju da proširite funkcionalnost posredne obrade.

Rad sa statičnim fajlovima

Go-ova standardna biblioteka uključuje mogućnosti za rad sa statičnim sadržajem, uključujući slike, kao i JavaScript i CSS datoteke. Njima se može pristupiti preko funkcije http.FileServer. Vraća obrađivač koji distribuira 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

Obavezno zapamtite da http.Dir prikazuje sadržaj direktorija ako ne sadrži glavnu datoteku index.html. U ovom slučaju, da biste spriječili kompromitaciju direktorija, trebali biste koristiti paket unindexed.

Ispravno gašenje

Go takođe ima takvu funkciju kao što je graciozno gašenje HTTP servera. Ovo se može uraditi pomoću metode Shutdown(). Server se pokreće u goroutini, a zatim se osluškuje kanal da 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 sa gotovo univerzalnom standardnom bibliotekom. Njegove zadane mogućnosti su vrlo široke, a možete ih ojačati uz pomoć interfejsa - to vam omogućava da razvijete zaista pouzdane HTTP servere.

Skillbox preporučuje:

izvor: www.habr.com

Dodajte komentar