
Prije pet godina počeo sam , to mi je omogućilo da učim Golang. Shvatio sam da je Go moćan jezik čije mogućnosti dopunjuju brojni alati. Go je univerzalan: posebno, uz njegovu pomoć lako je razvijati serverske aplikacije.
Ovaj članak posvećen je pisanju servera na Go-u. Počnimo s jednostavnim stvarima, kao što je "Hello world!", a završimo aplikacijom koja ima sljedeće mogućnosti:
— Korištenje Let’s Encrypt za HTTPS.
— Rad kao API rutera.
— Rad s middleware-om.
— Obrada statičnih datoteka.
— Ispravno zatvaranje rada.
Skillbox preporučuje: Praktični tečaj .
Podsjećamo: za sve čitatelje "Habra" — popust od 10 000 rubalja prilikom upisa na bilo koji tečaj Skillbox uz promo kod "Habr".
Hello, world!
Brzo možete stvoriti web server na Go-u. Evo primjera korištenja handlera koji vraća ranije spomenuti "Hello, world!".
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 , odmah ćete vidjeti tekst "Hello, world!" (naravno, ako sve ispravno radi).
Zatim ćemo više puta koristiti handler, ali prvo hajde da shvatimo kako sve to funkcioniše.
net/http
U primjeru se koristi paket , to je osnovno sredstvo u Go-u za razvoj i servera i HTTP klijenata. Da bismo razumjeli kod, razjasnimo značenje tri važna elementa: http.Handler, http.ServeMux i http.Server.
HTTP handleri
Kada primimo zahtjev, handler ga analizira i formira odgovor. Handleri u Go-u su implementirani na sljedeći način:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}U prvom primjeru koristi se pomoćna funkcija http.HandleFunc. Ona omotava drugu funkciju koja, zauzvrat, prima http.ResponseWriter i http.Request u ServeHTTP.
Drugim riječima, handleri u Golangu predstavljeni su jedinstvenim interfejsom, što programerima pruža brojne mogućnosti. Na primjer, middleware je implementiran putem handlera, gdje ServeHTTP prvo radi nešto, a zatim poziva metodu ServeHTTP drugog handlera.
Kao što je obenuto, handleri jednostavno formiraju odgovore na zahtjeve. No, koji točno handler treba koristiti u određenom trenutku?
Ruting zahtjeva
Kako biste donijeli pravu odluku, koristite HTTP multiplexor. U nekim bibliotekama nazivaju ga muxer ili router, no sve je to isto. Funkcija multiplexora je analizirati putanju zahtjeva i odabrati odgovarajući obrađivač.
Ako je potrebna podrška za složeno usmjeravanje, bolje je koristiti vanjske biblioteke. Neke od najnaprednijih su i , ove biblioteke omogućuju realizaciju međuprocesne obrade bez posebnih problema. Uz njihovu pomoć možete postaviti wildcard usmjeravanje i izvršiti niz drugih zadataka. Njihova prednost je kompatibilnost sa standardnim HTTP obrađivačima. Kao rezultat toga, možete napisati jednostavan kod s mogućnošću njegove modifikacije u budućnosti.
Rad sa složenim okvirima u običnim situacijama zahtijevat će ne posve standardna rješenja, što značajno otežava korištenje zadnjih obrađivača. Za izradu velike većine aplikacija dovoljna je kombinacija zadane biblioteke i jednostavnog usmjerivača.
Obrada zahtjeva
Osim toga, potrebna nam je komponenta koja će "slušati" dolazne veze i preusmjeravati sve zahtjeve pravom obrađivaču. Ovaj zadatak bez poteškoća će obaviti http.Server.
Ispod je prikazano da server odgovara za sve zadatke koji se odnose na obradu veza. To uključuje rad s TLS protokolom. Za realizaciju poziva http.ListenAndServe koristi se standardni HTTP server.
Sada pogledajmo složenije primjere.
Dodavanje Let’s Encrypt
Prema zadanim postavkama, naša aplikacija radi putem HTTP protokola, međutim preporučuje se koristiti HTTPS protokol. U Go-u se to može lako učiniti. Ako ste dobili certifikat i privatni ključ, dovoljnu je navesti ListenAndServeTLS s pravilnim datotekama certifikata i ključa.
http.ListenAndServeTLS(":443", "cert.pem", "key.pem", nil)Uvijek se može bolje.
nudi besplatne certifikate s mogućnošću automatskog obnavljanja. Da biste koristili uslugu, potreban je paket autocert.
Najlakši način da ga postavite je koristiti metodu autocert.NewListener u kombinaciji s http.Serve. Metoda omogućuje dobivanje i obnavljanje TLS certifikata, dok HTTP server obrađuje zahtjeve:
http.Serve(autocert.NewListener("example.com"), nil)Ako otvorimo u pregledniku , dobijamo HTTPS odgovor „Hello, world!“.
Ako je potrebna detaljnija konfiguracija, vrijedi koristiti autocert.Manager. Zatim kreiramo vlastiti instancu http.Server (do sada smo koristili zadanu) i dodajemo menadžer u 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("", "")Ovo je jednostavan način za implementaciju potpune podrške za HTTPS s automatskim obnavljanjem certifikata.
Dodavanje nestandardnih ruta
Zadani usmjerivač uključen u standardnu biblioteku je dobar, ali je vrlo jednostavan. U većini aplikacija potrebna je složenija usmjeravanje, uključujući ugniježdene i wildcard rute ili postupak postavljanja predložaka i parametara putanje.
U ovom slučaju vrijedi koristiti pakete i . S posljednjim ćemo naučiti raditi — u nastavku je prikazan primjer.
Imamo — datoteku api/v1/api.go, koja 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
}Postavljamo prefiks za rute api/vq u glavnoj datoteci.
Tada to možemo montirati na naš glavni usmjerivač pod prefiksom api/v1 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())Jednostavnost rada sa složenim rutama u Go omogućava pojednostavljenje strukture održavanja velikih kompleksnih aplikacija.
Rad s middleware-om
U slučaju međuprosječivanja koristi se omotač jednog HTTP obrađivača drugim, što omogućava brzo provoditi autentifikaciju, kompresiju, logiranje i neke druge funkcije.
Kao primjer razmotrit ćemo sučelje http.Handler, pomoću kojeg ćemo napisati obrađivač s autentifikacijom korisnika servisa.
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
}
// Pretpostavljajući da je autentifikacija prošla, pokreni izvorni obrađivač
next.ServeHTTP(w, r)
})
}Postoje i vanjski usmjerivači, poput chi, koji omogućuju proširenje funkcionalnosti međuprosječivanja.
Rad sa statičkim datotekama
Standardna biblioteka Go sadrži mogućnosti za rad sa statičkim sadržajem, uključujući slike, kao i JavaScript i CSS datoteke. Pristup njima može se dobiti putem funkcije http.FileServer. Ona vraća obrađivač koji isporučuje datoteke iz određenog direktorija.
func NewRouter() http.Handler {
router := chi.NewRouter()
r.Get("/{name}", HelloName)
// Konfiguracija dijeljenja statičkih datoteka
staticPath, _ := filepath.Abs("..\/..\/static\/\")
fs := http.FileServer(http.Dir(staticPath))
router.Handle("/*", fs)
return rObavezno treba imati na umu da http.Dir prikazuje sadržaj direktorija u slučaju da nema glavne datoteke index.html. U tom slučaju, kako bi se spriječila kompromitacija direktorija, treba koristiti paket .
Ispravno zatvaranje rada
U Go-u postoji i funkcija za ispravno zatvaranje HTTP poslužitelja. To se može učiniti pomoću metode Shutdown(). Poslužitelj se pokreće u gorutini, a zatim se kanal nadgleda za primanje signala prekida. Čim signal bude primljen, poslužitelj 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...))
}()
// Čekanje na prekid
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c
// Pokušaj ispravnog zatvaranja
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
def defer cancel()
srv.Shutdown(ctx)Kao zaključak
Go je moćan jezik s praktički univerzalnom standardnom bibliotekom. Njegove zadane mogućnosti su vrlo široke, a mogu se poboljšati pomoću sučelja — to omogućuje razvoj doista pouzdanih HTTP poslužitelja.
Skillbox preporučuje:
- Dvoipogodišnji praktični tečaj .
- Online edukacijski tečaj .
- Praktični godišnji tečaj .
Izvor: habr.com
