Sviluppà servitori web in Golang - da simplice à cumplessu

Sviluppà servitori web in Golang - da simplice à cumplessu

Cinque anni fà aghju cuminciatu sviluppà Gophish, hà permessu di amparà Golang. Aghju realizatu chì Go hè una lingua putente, chì hè cumplementata da parechje biblioteche. Go hè versatile: in particulare, pudete facilmente sviluppà applicazioni di u servitore cun ellu.

Questu articulu hè di scrive un servitore in Go. Cuminciamu cù cose simplici cum'è "Hello world!" è finiscinu cù una applicazione cù queste caratteristiche:

- Utilizendu Let's Encrypt per HTTPS.
- U travagliu cum'è un router API.
- U travagliu cù middleware.
- Gestione di fugliali statichi.
- Arregu currettu.

Skillbox consiglia: Corso praticu "Sviluppatore Python da zero".

Ramintemu: per tutti i lettori di "Habr" - un scontu di 10 000 rubles quandu si iscrizzione in ogni cursu Skillbox cù u codice promozionale "Habr".

Bonghjornu, mondu!

A creazione di un servitore web in Go pò esse assai veloce. Eccu un esempiu di utilizà un gestore chì torna u "Hello, world!" prumessu sopra.

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

Dopu à quessa, si eseguite l'applicazione è apre a pagina localhost, tandu vi vede subitu u testu "Hello, world!" (di sicuru, se tuttu funziona bè).

Adupremu u gestore ripetutamente in u seguitu, ma prima capemu cumu tuttu funziona.

net/http

L'esempiu hà utilizatu u pacchettu net/http, hè u strumentu primariu di Go per sviluppà i servitori è i clienti HTTP. Per capisce u codice, capiscenu u significatu di trè elementi impurtanti: http.Handler, http.ServeMux è http.Server.

I gestori HTTP

Quandu ricevemu una dumanda, u gestore l'analiza è genera una risposta. I gestori in Go sò implementati cusì:

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

U primu esempiu usa a funzione http.HandleFunc helper. Imballa una altra funzione chì in turnu accetta http.ResponseWriter è http.Request in ServeHTTP.

In altri palori, i gestori in Golang sò rapprisentati da una sola interfaccia, chì dà assai opportunità per u programatore. Cusì, per esempiu, u middleware hè implementatu cù un gestore, induve ServeHTTP prima face qualcosa è poi chjama u metudu ServeHTTP di un altru gestore.

Cum'è l'esitatu sopra, i gestori solu formanu risposte à e dumande. Ma quale gestore deve esse usatu in un mumentu particulare?

Richiesta di routing

Per fà a scelta bona, utilizate u multiplexer HTTP. In una quantità di biblioteche hè chjamatu muxer o router, ma sò tutti listessi. A funzione di u multiplexer hè di analizà a strada di a dumanda è selezziunate u gestore adattatu.

Sè avete bisognu di supportu per u routing cumplessu, allora hè megliu aduprà biblioteche di terzu. Unu di i più avanzati gorilla/mux и go-chi/chi, sti biblioteche facenu pussibile implementà u prucessu intermediu senza prublemi. Cù u so aiutu, pudete stabilisce u routing wildcard è eseguisce una quantità di altre attività. U so vantaghju hè a cumpatibilità cù i manipulatori HTTP standard. In u risultatu, pudete scrive un codice simplice chì pò esse mudificatu in u futuru.

U travagliu cù frameworks cumplessi in una situazione normale richiederà suluzioni micca abbastanza standard, è questu complica assai l'usu di i gestori predeterminati. A cumminazzioni di a biblioteca predeterminata è un router simplice serà abbastanza per creà a maiò parte di l'applicazioni.

Trattamentu di e dumande

Inoltre, avemu bisognu di un cumpunente chì "ascolta" per e cunnessione entranti è redirige tutte e dumande à u gestore currettu. http.Server pò facilmente affruntà stu compitu.

I seguenti mostra chì u servitore hè rispunsevule per tutti i travaglii ligati à a gestione di e cunnessione. Questu hè, per esempiu, u travagliu nantu à u protocolu TLS. Un servitore HTTP standard hè utilizatu per implementà a chjama http.ListenAndServer.

Avà fighjemu esempi più cumplessi.

Aghjunghjendu Let's Encrypt

Per automaticamente, a nostra applicazione corre nantu à u protocolu HTTP, ma hè cunsigliatu di utilizà u protocolu HTTPS. In Go, questu pò esse fattu senza prublemi. Sè avete ricevutu un certificatu è una chjave privata, allora hè abbastanza per scrive ListenAndServeTLS cù u certificatu currettu è i schedarii chjave.

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

Pudete sempre fà megliu.

Let's Crypt dà certificati gratuiti cù a pussibilità di rinnuvamentu automaticu. Per utilizà u serviziu, avete bisognu di un pacchettu autocert.

A manera più faciule di stallà hè di utilizà u metudu autocert.NewListener in cumminazione cù http.Serve. U metudu permette di riceve è rinnuvà i certificati TLS mentre u servitore HTTP processa e dumande:

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

Sè avemu apertu in u navigatore example.com, avemu una risposta HTTPS "Hello, world!".

Sè avete bisognu di cunfigurazione più dettagliata, allora avete aduprà l'autocert.Manager. Allora creamu u nostru propiu http.Server instance (finu à avà l'avemu utilizatu per difettu) è aghjunghje u manager à u servitore TLSConfig:

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

Questu hè un modu faciule per implementà un supportu HTTPS cumpletu cù rinnuvamentu automaticu di certificati.

Aghjunghjendu Routes Custom

U router predeterminatu inclusu in a biblioteca standard hè bellu, ma assai basicu. A maiò parte di l'applicazioni necessitanu un routing più cumplessu, cumprese rotte nidificate è wildcard, o a prucedura per stabilisce mudelli di percorsi è paràmetri.

In questu casu, avete aduprà pacchetti gorilla/mux и go-chi/chi. Avemu da amparà cumu travaglià cù l'ultime - un esempiu hè mostratu quì sottu.

Hè datu u schedariu api/v1/api.go chì cuntene e rotte per a nostra 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
}

Avemu stabilitu u prefissu api/vq per e rotte in u schedariu principale.

Pudemu allora muntà questu à u nostru router principale sottu u prefissu api/v1/ in a nostra applicazione principale:

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

A facilità di travaglià cù rotte cumplessi in Go permette di simplificà a strutturazione è u mantenimentu di grandi applicazioni cumplessi.

U travagliu cù middleware

In u casu di u prucessu intermediu, l'imbulighjate un handler HTTP cù un altru hè utilizatu, chì permette di realizà rapidamente l'autentificazione, a compressione, u logging è alcune altre funzioni.

Per esempiu, cunsideremu l'interfaccia http.Handler, cù u so aiutu scriveremu un handler cù l'autentificazione di l'utilizatori di serviziu.

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

Ci sò routers di terzu partitu, cum'è chi, chì permettenu di allargà a funziunalità di u prucessu intermediariu.

U travagliu cù i schedari statici

A biblioteca standard di Go include facilità per travaglià cù cuntenutu staticu, cumprese l'imaghjini, è ancu i schedari JavaScript è CSS. Si ponu accede à traversu a funzione http.FileServer. Ritorna un gestore chì distribuisce i schedari da un cartulare specificu.

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

Assicuratevi di ricurdà chì http.Dir mostra u cuntenutu di u cartulare s'ellu ùn cuntene micca u schedariu principale index.html. In questu casu, per prevene u repertoriu da esse cumprumessi, duvete aduprà u pacchettu unindexed.

Arregu currettu

Go hà ancu una tale funzione cum'è un chjusu graziosu di u servitore HTTP. Questu pò esse fattu cù u metudu Shutdown (). U servitore hè cuminciatu in una goroutine, è dopu u canali hè intesu per riceve un signalu di interruzzione. Appena u signale hè ricivutu, u servitore si spegne, ma micca subitu, ma dopu à pocu seconde.

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)

Cum'è cunclusione

Go hè una lingua putente cù una biblioteca standard quasi universale. E so capacità predeterminate sò assai largu, è pudete rinfurzà cù l'aiutu di l'interfacce - questu permette di sviluppà servitori HTTP veramente affidabili.

Skillbox consiglia:

Source: www.habr.com

Add a comment