Zhvillimi i serverit të uebit në Golang - nga i thjeshtë në kompleks

Zhvillimi i serverit të uebit në Golang - nga i thjeshtë në kompleks

Pesë vjet më parë fillova zhvillojnë Gophish, kjo ofroi një mundësi për të mësuar Golang. Kuptova se Go është një gjuhë e fuqishme, e plotësuar nga shumë biblioteka. Go është e gjithanshme: në veçanti, mund të përdoret për të zhvilluar aplikacione nga ana e serverit pa asnjë problem.

Ky artikull ka të bëjë me shkrimin e një serveri në Go. Le të fillojmë me gjëra të thjeshta si "Përshëndetje botë!" dhe të përfundojmë me një aplikacion me aftësitë e mëposhtme:

- Përdorimi i Let's Encrypt për HTTPS.
— Duke punuar si një ruter API.
— Puna me programin e mesëm.
— Përpunimi i skedarëve statikë.
— Mbyllja e saktë.

Skillbox rekomandon: Kurse praktike "Zhvilluesi i Python nga e para".

Kujtojmë: për të gjithë lexuesit e "Habr" - një zbritje prej 10 rubla kur regjistroheni në çdo kurs Skillbox duke përdorur kodin promovues "Habr".

Përshendetje Botë!

Mund të krijoni një server në internet në Go shumë shpejt. Këtu është një shembull i përdorimit të një mbajtësi që kthen "Përshëndetje, botë!" të premtuar më sipër.

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 ekzekutoni aplikacionin dhe hapni faqen localhost, atëherë menjëherë do të shihni tekstin "Përshëndetje, botë!" (Nëse gjithçka funksionon siç duhet, sigurisht).

Ne do ta përdorim mbajtësin disa herë më vonë, por së pari le të kuptojmë se si funksionon gjithçka.

net/http

Shembulli përdori paketën net/http, është mjeti kryesor në Go për zhvillimin e serverëve dhe klientëve HTTP. Për të kuptuar kodin, le të kuptojmë kuptimin e tre elementëve të rëndësishëm: http.Handler, http.ServeMux dhe http.Server.

Trajtuesit HTTP

Kur marrim një kërkesë, trajtuesi e analizon atë dhe gjeneron një përgjigje. Trajtuesit në Go zbatohen si më poshtë:

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

Shembulli i parë përdor funksionin ndihmës http.HandleFunc. Ai mbështjell një funksion tjetër, i cili nga ana e tij merr http.ResponseWriter dhe http.Request në ServeHTTP.

Me fjalë të tjera, mbajtësit në Golang paraqiten në një ndërfaqe të vetme, e cila i jep shumë opsione programuesit. Kështu, për shembull, Middleware zbatohet duke përdorur një mbajtës, ku ServeHTTP fillimisht bën diçka dhe më pas thërret metodën ServeHTTP të një mbajtës tjetër.

Siç u përmend më lart, trajtuesit thjesht gjenerojnë përgjigje ndaj kërkesave. Por cili mbajtës i veçantë duhet të përdoret në një moment të caktuar kohor?

Kërkoni për rrugëtim

Për të bërë zgjedhjen e duhur, përdorni një multiplekser HTTP. Në një numër bibliotekash quhet muxer ose router, por të gjitha janë e njëjta gjë. Funksioni i multiplekserit është të analizojë rrugën e kërkesës dhe të zgjedhë mbajtësin e duhur.

Nëse keni nevojë për mbështetje për rrugëtim kompleks, atëherë është më mirë të përdorni bibliotekat e palëve të treta. Disa nga më të avancuarit - gorilla/mux и go-chi/chi, këto biblioteka bëjnë të mundur zbatimin e përpunimit të ndërmjetëm pa asnjë problem. Me ndihmën e tyre, ju mund të konfiguroni drejtimin e shkronjave të egra dhe të kryeni një sërë detyrash të tjera. Avantazhi i tyre është përputhshmëria me mbajtësit standardë HTTP. Si rezultat, mund të shkruani kod të thjeshtë që mund të modifikohet në të ardhmen.

Puna me korniza komplekse në një situatë normale do të kërkojë zgjidhje jo standarde dhe kjo e ndërlikon ndjeshëm përdorimin e mbajtësve të paracaktuar. Për të krijuar shumicën dërrmuese të aplikacioneve, do të mjaftojë një kombinim i bibliotekës së paracaktuar dhe një ruteri i thjeshtë.

Përpunimi i pyetjeve

Për më tepër, ne kemi nevojë për një komponent që do të "dëgjojë" për lidhjet hyrëse dhe do t'i ridrejtojë të gjitha kërkesat në trajtuesin e duhur. http.Server mund ta trajtojë lehtësisht këtë detyrë.

Më poshtë tregon se serveri është përgjegjës për të gjitha detyrat që lidhen me përpunimin e lidhjes. Kjo, për shembull, funksionon duke përdorur protokollin TLS. Për të zbatuar thirrjen http.ListenAndServer, përdoret një server standard HTTP.

Tani le të shohim shembuj më kompleksë.

Shtimi Let's Encrypt

Si parazgjedhje, aplikacioni ynë funksionon mbi protokollin HTTP, por rekomandohet përdorimi i protokollit HTTPS. Kjo mund të bëhet pa probleme në Go. Nëse keni marrë një certifikatë dhe çelës privat, atëherë mjafton të regjistroni ListenAndServeTLS me certifikatën e duhur dhe skedarët e çelësave.

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

Ju gjithmonë mund të bëni më mirë.

Le të Encrypt ofron certifikata falas me rinovim automatik. Për të përdorur shërbimin, ju duhet një paketë autocert.

Mënyra më e lehtë për ta konfiguruar është përdorimi i metodës autocert.NewListener në kombinim me http.Serve. Metoda ju lejon të merrni dhe përditësoni certifikatat TLS ndërsa serveri HTTP përpunon kërkesat:

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

Nëse hapim në shfletues example.com, do të marrim një përgjigje HTTPS "Përshëndetje, botë!"

Nëse keni nevojë për konfigurim më të detajuar, atëherë duhet të përdorni menaxherin autocert.Manager. Më pas ne krijojmë shembullin tonë http.Server (deri tani e kemi përdorur si parazgjedhje) dhe shtojmë menaxherin në serverin 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("", "")

Kjo është një mënyrë e thjeshtë për të zbatuar mbështetjen e plotë të HTTPS me rinovimin automatik të certifikatës.

Shtimi i rrugëve të personalizuara

Ruteri i paracaktuar i përfshirë në bibliotekën standarde është i mirë, por është shumë themelor. Shumica e aplikacioneve kërkojnë një rrugëtim më kompleks, duke përfshirë rrugët e ndërthurura dhe të ngurta, ose një procedurë për vendosjen e modeleve dhe parametrave të shtigjeve.

Në këtë rast ia vlen të përdorni paketa gorilla/mux и go-chi/chi. Ne do të mësojmë se si të punojmë me këtë të fundit - një shembull është treguar më poshtë.

Është dhënë skedari api/v1/api.go që përmban rrugë 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
}

Ne vendosëm prefiksin api/vq për rrugët në skedarin kryesor.

Më pas mund ta montojmë këtë në ruterin tonë kryesor nën prefiksin api/v1/ përsëri 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())

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

Puna me programe të mesme

Skenimi përfshin mbështjelljen e një mbajtësi HTTP me një tjetër, duke bërë të mundur kryerjen e shpejtë të vërtetimit, kompresimit, regjistrimit dhe disa funksioneve të tjera.

Si shembull, le të shohim ndërfaqen http.Handler; ne do ta përdorim atë për të shkruar një mbajtës që vërteton përdoruesit e 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
        }
        // Assuming authentication passed, run the original handler
        next.ServeHTTP(w, r)
    })
}

Ka ruterë të palëve të treta, të tilla si chi, që ju lejojnë të zgjeroni funksionalitetin e programit të mesëm.

Puna me skedarë statikë

Biblioteka standarde Go përfshin aftësi për të punuar me përmbajtje statike, duke përfshirë imazhet, skedarët JavaScript dhe CSS. Ato mund të aksesohen përmes funksionit http.FileServer. Ai kthen një mbajtës që shërben skedarë nga një drejtori specifike.

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

Padyshim që vlen të kujtohet se http.Dir shfaq përmbajtjen e drejtorisë nëse nuk përmban skedarin kryesor index.html. Në këtë rast, për të parandaluar komprometimin e drejtorisë, duhet të përdorni paketën unindexed.

Mbyllja e saktë

Go ka gjithashtu një veçori të quajtur mbyllje e këndshme e serverit HTTP. Kjo mund të bëhet duke përdorur metodën Shutdown(). Serveri niset në një gorutinë dhe më pas kanali dëgjohet për të marrë një sinjal ndërprerjeje. Sapo të merret sinjali, serveri fiket, por jo menjëherë, por pas disa sekondash.

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)

Si përfundim

Go është një gjuhë e fuqishme me një bibliotekë standarde pothuajse universale. Aftësitë e tij të paracaktuara janë shumë të gjera dhe ato mund të përmirësohen duke përdorur ndërfaqe - kjo ju lejon të zhvilloni serverë vërtet të besueshëm HTTP.

Skillbox rekomandon:

Burimi: www.habr.com

Shto një koment