Zhvillimi i serverĂ«ve web me Golang — nga e thjeshta nĂ« tĂ« avancuar

Zhvillimi i serverĂ«ve web me Golang — nga e thjeshta nĂ« tĂ« avancuar

Pesë vjet më parë fillova të zhvilloj Gophish, kjo më dha mundësinë të studioj Golang. Kuptova se Go është një gjuhë e fuqishme, mundësitë e së cilës plotësohen nga shumë biblioteka. Go është universale: veçanërisht, me të mund të zhvillohet lehtësisht aplikacione serverike.

Ky artikull i kushtohet shkrimit të një serveri me Go. Le të fillojmë me gjëra të thjeshta, si "Hello world!", dhe të përfundojmë me një aplikacion me këto mundësi:

— PĂ«rdorimi i Let’s Encrypt pĂ«r HTTPS.
— Funksionimi si njĂ« API-router.
— Puna me middleware.
— Trajtimi i skedarĂ«ve statikĂ«.
— Mbyllja e saktĂ« e punĂ«s.

Skillbox rekomandon: Kurs praktik «Programues Python nga zero».

KujtojmĂ«: pĂ«r tĂ« gjithĂ« lexuesit e "Habra" — zbritje prej 10,000 rublej pĂ«r regjistrimin nĂ« çdo kurs Skillbox me kodin promocional "Habr".

Hello, world!

Krijimi i një serveri web në Go mund të bëhet shumë shpejt. Ja një shembull i përdorimit të një përpunuesi që kthen "Hello, world!" siç u përmend më parë.

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

Pas kësaj, nëse e lançoni aplikacionin dhe hapni faqen localhost, do të shihni menjëherë tekstin "Hello, world!" (sigurisht, nëse gjithçka funksionon siç duhet).

Më vonë ne do të përdorim vazhdimisht menaxherin, por fillimisht le të kuptojmë se si funksionon çdo gjë.

net/http

Në shembull u përdor paketa net/http, që është mjete kryesore në Go për zhvillimin e serverëve dhe klientëve HTTP. Për të kuptuar kodin, le të shqyrtojmë kuptimin e tre elementeve të rëndësishëm: http.Handler, http.ServeMux dhe http.Server.

Menaxherët HTTP

Kur marrim një kërkesë, menaxheri e analizon atë dhe formon një përgjigje. Menaxherët në Go implementohen si më poshtë:

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

Në shembullin e parë përdoret funksioni ndihmës http.HandleFunc. Ky funksion e mb wrapped një funksion tjetër, i cili, nga ana e tij, merr http.ResponseWriter dhe http.Request në ServeHTTP.

Me fjalë të tjera, menaxherët në Golang përfaqësohen nga një ndërfaqe e vetme, që ofron shumë mundësi për programuesin. Për shembull, middleware implementohet përmes menaxherit, ku ServeHTTP fillimisht bën diçka dhe pastaj thërret metodën ServeHTTP të një menaxheri tjetër.

Siç u përmend më sipër, menaxherët thjesht formulojnë përgjigje për kërkesat. Por cili menaxher duhet të përdoret në një moment të caktuar?

Rruga e kërkesave

Për të bërë një zgjedhje të saktë, përdorni HTTP-multiplexorin. Në disa biblioteka, ai quhet muxer ose router, por të gjithë janë njësoj. Funksioni i multiplexorit është analiza e rrugës së kërkesës dhe zgjedhja e trajtuesit përkatës.

Nëse është e nevojshme mbështetje për ruterim të komplikuar, atëherë është më mirë të përdoren biblioteka të jashtme. Disa nga më të avancuarat janë: gorilla/mux dhe go-chi/chi, këto biblioteka ofrojnë mundësi për të realizuar përpunimin ndërmjetës pa ndonjë problem të madh. Me ndihmën e tyre, mund të konfiguroni ruterimin wildcard dhe të përfundoni një sërë detyrash të tjera. Avantazhi i tyre është përputhshmëria me trajtuesit standardë HTTP. Si rezultat, mund të shkruani kod të thjeshtë me mundësi modifikimi në të ardhmen.

Të punosh me framework-e të komplikuara në situata të zakonshme kërkon zgjidhje jo standarde, dhe kjo e komplikon dukshëm përdorimin e trajtuesve default. Për krijimin e shumicës dërrmuese të aplikacioneve, një kombinim i bibliotekës për default dhe një ruter të thjeshtë është mjaftueshëm.

Përpunimi i kërkesave

Përveç kësaj, na nevojitet një komponent që do të "dëgjojë" lidhjet e ardhshme dhe do të drejtojë të gjitha kërkesat te trajtuesi i duhur. Këtë detyrë e zgjidh lehtësisht http.Server.

Më poshtë tregohet se serveri është përgjegjës për të gjitha detyrat që lidhen me përpunimin e lidhjeve. Kjo është, për shembull, puna sipas protokollit TLS. Për të realizuar thirrjen http.ListenAndServer përdoret serveri standard HTTP.

Tani le të shqyrtojmë shembuj më të ndërlikuar.

Shtimi i Let’s Encrypt

Në parazgjedhje, aplikacioni ynë punon sipas protokollit HTTP, por është e rekomanduar të përdoret protokolli HTTPS. Në Go, kjo mund të bëhet pa asnjë problem. Nëse keni marrë një certifikatë dhe një çelës privat, atëherë mjafton të shkruani ListenAndServeTLS duke treguar skedarët e duhur të certifikatës dhe çelësit.

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

Gjithmonë mund të bëhet më mirë.

Let’s Encrypt ofron certifikata falas me mundĂ«si pĂ«r rinovim automatik. PĂ«r tĂ« shfrytĂ«zuar shĂ«rbimin, nevojitet paketi autocert.

Mënyra më e lehtë për ta konfigurimin është të përdorni metodën autocert.NewListener në kombinim me http.Serve. Kjo metodë lejon marrjen dhe rinovimin e certifikatave TLS, ndërsa serveri HTTP përpunon kërkesat:

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

Nëse hapim në shfletuesin tonë example.com, do të marrim përgjigjen HTTPS "Hello, world!".

Nëse na nevojitet një konfigurim më i detajuar, është mirë të përdorim menaxherin autocert.Manager. Pastaj krijojmë një instancë të vetme http.Server (deri tani e kemi përdorur atë në mënyrë default) dhe shtojmë menaxherin në TLSConfig të serverit:

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

Ky është një mënyrë e thjeshtë për të realizuar mbështetje të plotë HTTPS me rinovim automatik të certifikateve.

Shtimi i rrugeve të papërshtatshme

Routeri default, i përfshirë në bibliotekën standarde, është i mirë, por shumë i thjeshtë. Në shumicën e aplikacioneve, nevojitet një rrugëzim më kompleks, duke përfshirë rruge të thella dhe wildcard ose procedura për vendosjen e modeleve dhe parametrave të rrugëve.

NĂ« kĂ«tĂ« rast, Ă«shtĂ« mirĂ« tĂ« pĂ«rdoren paketa gorilla/mux dhe go-chi/chi. MĂ« e fundit do tĂ« mĂ«sojmĂ« tĂ« punojmĂ« — mĂ« poshtĂ« Ă«shtĂ« njĂ« shembull.

ËshtĂ« e dhĂ«nĂ« — skedari api/v1/api.go, i cili pĂ«rmban rrugĂ«t pĂ«r API-nĂ« tonĂ«:

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

Vendosim një prefiks për rrugët api/vq në skedarin kryesor.

Ne mund ta ngremë këtë në routerin tonë kryesor nën prefiksin api/v1 në aplikacionin tonë kryesor:

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

Thjeshtësia e punës me rrugët komplekse në Go e bën të mundur thjeshtimin e strukturimit të menaxhimit të aplikacioneve të mëdha dhe komplekse.

Puna me middleware

Në rastin e përpunimit të ndërmjetëm, përdoret mbështjellja e një kujdestari HTTP me një tjetër, e cila e bën të mundur të kryhen shpejt autentifikimi, kompresimi, regjistrimi dhe disa funksione të tjera.

Si një shembull, le të shqyrtojmë interfacën http.Handler, me të cilën do të shkruajmë një kujdestar me autentifikimin e përdoruesve të shërbimit.

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
        }
        // Duke supozuar se autentifikimi kaloi, ekzekutoni kujdestarin origjinal
        next.ServeHTTP(w, r)
    })
}

Ekzistojnë routerë të palës së tretë, për shembull, chi, të cilët lejojnë zgjerimin e funksionalitetit të përpunimit të ndërmjetëm.

Puna me skedarët statikë

Biblioteka standarde e Go përfshin mundësi për të punuar me përmbajtje statike, duke përfshirë imazhe, si dhe skedarë JavaScript dhe CSS. Në to mund të qaseni përmes funksionit http.FileServer. Ai kthen një përfaqësues që shpërndan skedarët nga një directory të caktuar.

func NewRouter() http.Handler {
    router := chi.NewRouter()
    r.Get("/{name}", HelloName)
 
// Konfigurimi i shpërndarjes së skedarëve statikë
staticPath, _ := filepath.Abs("../../static/")
fs := http.FileServer(http.Dir(staticPath))
    router.Handle("/*", fs)
    
    return r

Eshtë e domosdoshme të kujtohet se http.Dir e ekspozon përmbajtjen e katalogut në rast se nuk ka skedarin kryesor index.html. Në këtë rast, për të parandaluar komprometimin e katalogut, është mirë të përdorni paketën pa indekse.

Përfundim korrekt

Në Go ekziston gjithashtu funksioni për përfundimin e duhur të serverit HTTP. Kjo mund të bëhet përmes metodës Shutdown(). Serveri niset në një gorutinë, dhe më pas një kanal monitorohet për të pranuar sinjalin e ndërprerjes. Sapo merr sinjalin, serveri çaktivizohet, por jo menjëherë, për disa sekonda.

handler := server.NewRouter()
srv := &http.Server{
    Handler: handler,
}
 
go func() {
srv.Serve(autocert.NewListener(domains...))
}()
 
// Pristep për një ndërprerje
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c
 
// Përpiquni një shuarje të qetë
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
srv.Shutdown(ctx)

Si përfundim

Go Ă«shtĂ« njĂ« gjuhĂ« e fuqishme me njĂ« bibliotekĂ« standarde praktikisht universale. Ajo ofron mundĂ«si tĂ« pĂ«rkryer, dhe kĂ«to mund tĂ« theksohen edhe mĂ« shumĂ« pĂ«rmes ndĂ«rfaqeve — kjo lejon zhvillimin e serverĂ«ve HTTP me tĂ« vĂ«rtetĂ« tĂ« besueshĂ«m.

Skillbox rekomandon:

Burimi: habr.com

Bleni hostim tĂ« besueshĂ«m pĂ«r faqe me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Bleni hostim tĂ« besueshĂ«m pĂ«r faqe me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster