Vývoj webových serverů v Golangu - od jednoduchých po komplexní

Vývoj webových serverů v Golangu - od jednoduchých po komplexní

Před pěti lety jsem začal vyvinout Gophish, to poskytlo příležitost naučit se Golang. Uvědomil jsem si, že Go je mocný jazyk, doplněný mnoha knihovnami. Go je všestranný: zejména jej lze bez problémů použít k vývoji aplikací na straně serveru.

Tento článek je o psaní serveru v Go. Začněme jednoduchými věcmi jako „Ahoj světe!“ a skončeme aplikací s následujícími schopnostmi:

- Použití Let's Encrypt pro HTTPS.
— Pracuje jako API router.
— Práce s middlewarem.
— Zpracování statických souborů.
— Správné vypnutí.

Skillbox doporučuje: Praktický kurz "Vývojář Pythonu od nuly".

Připomínáme: pro všechny čtenáře "Habr" - sleva 10 000 rublů při zápisu do jakéhokoli kurzu Skillbox pomocí propagačního kódu "Habr".

Ahoj světe!

Webový server v Go můžete vytvořit velmi rychle. Zde je příklad použití handleru, který vrací „Ahoj, světe!“ slíbené výše.

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

Poté, pokud spustíte aplikaci a otevřete stránku localhost, pak okamžitě uvidíte text „Ahoj, světe!“ (pokud vše funguje správně, samozřejmě).

Obslužnou rutinu později použijeme několikrát, ale nejprve si porozuměme, jak vše funguje.

net/http

V příkladu byl použit balíček net/http, je to primární nástroj v Go pro vývoj serverů i HTTP klientů. Abychom porozuměli kódu, pochopme význam tří důležitých prvků: http.Handler, http.ServeMux a http.Server.

HTTP handlery

Když obdržíme požadavek, handler jej analyzuje a vygeneruje odpověď. Obslužné nástroje v Go jsou implementovány následovně:

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

První příklad používá pomocnou funkci http.HandleFunc. Zabaluje další funkci, která zase přebírá http.ResponseWriter a http.Request do ServeHTTP.

Jinými slovy, handlery v Golangu jsou prezentovány v jediném rozhraní, které dává programátorovi mnoho možností. Například middleware je implementován pomocí handleru, kde ServeHTTP nejprve něco udělá a pak zavolá metodu ServeHTTP jiného handleru.

Jak již bylo zmíněno výše, handleři jednoduše generují odpovědi na požadavky. Ale který konkrétní handler by měl být použit v konkrétním okamžiku?

Žádost o směrování

Pro správnou volbu použijte HTTP multiplexer. V řadě knihoven se tomu říká muxer nebo router, ale všechny jsou to samé. Funkcí multiplexeru je analyzovat cestu požadavku a vybrat vhodnou obsluhu.

Pokud potřebujete podporu pro složité směrování, pak je lepší použít knihovny třetích stran. Některé z nejpokročilejších - gorila/mux и go-chi/chi, tyto knihovny umožňují bez problémů implementovat mezizpracování. S jejich pomocí můžete nakonfigurovat směrování zástupných znaků a provádět řadu dalších úkolů. Jejich výhodou je kompatibilita se standardními HTTP handlery. Díky tomu můžete napsat jednoduchý kód, který lze v budoucnu upravit.

Práce se složitými frameworky v běžné situaci bude vyžadovat nestandardní řešení a to výrazně komplikuje použití výchozích handlerů. Pro vytvoření naprosté většiny aplikací bude stačit kombinace výchozí knihovny a jednoduchého routeru.

Zpracování dotazu

Kromě toho potřebujeme komponentu, která bude „naslouchat“ příchozím připojením a přesměrovat všechny požadavky na správný handler. http.Server tento úkol snadno zvládne.

Následující ukazuje, že server je odpovědný za všechny úkoly související se zpracováním připojení. To například funguje pomocí protokolu TLS. K implementaci volání http.ListenAndServer se používá standardní HTTP server.

Nyní se podívejme na složitější příklady.

Přidání Let's Encrypt

Ve výchozím nastavení naše aplikace běží přes protokol HTTP, ale je doporučeno používat protokol HTTPS. To lze bez problémů provést v Go. Pokud jste obdrželi certifikát a soukromý klíč, stačí zaregistrovat ListenAndServeTLS se správnými soubory certifikátu a klíčů.

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

Vždy to můžete udělat lépe.

Zašifrujeme poskytuje bezplatné certifikáty s automatickou obnovou. Abyste mohli službu využívat, potřebujete balíček autocert.

Nejjednodušší způsob, jak jej nakonfigurovat, je použít metodu autocert.NewListener v kombinaci s http.Serve. Tato metoda vám umožňuje získat a aktualizovat certifikáty TLS, zatímco HTTP server zpracovává požadavky:

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

Pokud otevřeme v prohlížeči example.com, obdržíme odpověď HTTPS „Ahoj, světe!“

Pokud potřebujete podrobnější konfiguraci, měli byste použít správce autocert.Manager. Poté vytvoříme vlastní instanci http.Server (dosud jsme ji používali ve výchozím nastavení) a přidáme správce na server 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("", "")

Jedná se o snadný způsob, jak implementovat plnou podporu HTTPS s automatickou obnovou certifikátu.

Přidání vlastních tras

Výchozí router zahrnutý ve standardní knihovně je dobrý, ale je velmi základní. Většina aplikací vyžaduje složitější směrování, včetně vnořených tras a tras se zástupnými znaky nebo postup pro nastavení vzorů a parametrů cest.

V tomto případě se vyplatí používat balíčky gorila/mux и go-chi/chi. Naučíme se pracovat s posledně jmenovaným - příklad je uveden níže.

Daný je soubor api/v1/api.go obsahující trasy pro naše 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
}

V hlavním souboru jsme nastavili předponu api/vq pro cesty.

To pak můžeme připojit k našemu hlavnímu routeru pod předponou api/v1/ zpět v naší hlavní aplikaci:

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

Snadná práce Go se složitými cestami umožňuje zjednodušit strukturování a údržbu velkých, komplexních aplikací.

Práce s middlewarem

Staging zahrnuje zabalení jednoho ovladače HTTP do druhého, což umožňuje rychle provádět ověřování, kompresi, protokolování a několik dalších funkcí.

Jako příklad se podívejme na rozhraní http.Handler, které použijeme k vytvoření handleru, který autentizuje uživatele služby.

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

Existují směrovače třetích stran, jako je chi, které vám umožňují rozšířit funkčnost middlewaru.

Práce se statickými soubory

Standardní knihovna Go obsahuje funkce pro práci se statickým obsahem, včetně obrázků, souborů JavaScript a CSS. Lze k nim přistupovat prostřednictvím funkce http.FileServer. Vrací obslužnou rutinu, která obsluhuje soubory z určitého adresáře.

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

Určitě stojí za připomenutí, že http.Dir zobrazuje obsah adresáře, pokud neobsahuje hlavní soubor index.html. V tomto případě byste měli použít balíček, abyste zabránili kompromitaci adresáře unindexed.

Správné vypnutí

Go má také funkci zvanou elegantní vypnutí serveru HTTP. To lze provést pomocí metody Shutdown(). Server je spuštěn v goroutine a poté je kanál poslouchán, aby přijal signál přerušení. Jakmile je signál přijat, server se vypne, ale ne okamžitě, ale po několika sekundách.

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)

Jako závěr

Go je výkonný jazyk s téměř univerzální standardní knihovnou. Jeho výchozí možnosti jsou velmi široké a lze je vylepšit pomocí rozhraní – to vám umožňuje vyvíjet skutečně spolehlivé servery HTTP.

Skillbox doporučuje:

Zdroj: www.habr.com

Přidat komentář