o recapitulare a solicitării de manipulare în Go

Ultima actualizare: 21 ianuarie 2018 Filed under: golang tutorial

procesarea cererilor HTTP cu Go este în primul rând despre două lucruri: ServeMuxes și Handlers.

un ServeMux este, în esență, un router cerere HTTP (sau multiplexor). Acesta compară cererile primite împotriva unei liste de căi URL predefinite, și solicită handler asociat pentru calea ori de câte ori se găsește o potrivire.

manipulatorii sunt responsabili pentru scrierea anteturilor și organismelor de răspuns. Aproape orice obiect poate fi un handler, atâta timp cât satisface http.Handler interfață. În termeni laici, asta înseamnă pur și simplu că trebuie să aibă o ServeHTTP metodă cu următoarea semnătură:

ServeHTTP(http.ResponseWriter, *http.Request)

pachetul HTTP Go este livrat cu câteva funcții pentru a genera Handlere comune, cum ar fi FileServerNotFoundHandler și RedirectHandler. Să începem cu un exemplu simplu, dar inventat:

$ mkdir handler-example$ cd handler-example$ touch main.go

Fișier: principal.du-te

package mainimport ( "log" "net/http")func main() { mux := http.NewServeMux() rh := http.RedirectHandler("http://example.org", 307) mux.Handle("/foo", rh) log.Println("Listening...") http.ListenAndServe(":3000", mux)}

să trecem prin asta repede:

  • În main funcția folosim http.NewServeMux funcția pentru a crea un servemux gol.
  • apoi folosimhttp.RedirectHandler funcția pentru a crea un nou handler. Acest handler 307 redirecționează toate cererile pe care le primește la http://example.org.
  • în continuare vom folosi mux.Handlefuncția de a înregistra acest lucru cu noul nostru ServeMux, deci acționează ca handler pentru toate cererile primite cu calea URL /foo.
  • în cele din urmă creăm un server nou și începem să ascultăm cererile primite cu funcțiahttp.ListenAndServe, trecând în ServeMux pentru ca acesta să se potrivească cererilor.

mergeți mai departe și rulați aplicația:

$ go run main.goListening...

și vizitațihttp://localhost:3000/foo în browser. Ar trebui să găsiți că solicitarea dvs. este redirecționată cu succes.

ochii de vultur ai observat ceva interesant: semnătura pentru funcția ListenAndServe esteListenAndServe(addr string, handler Handler), dar am trecut un ServeMux ca al doilea parametru.

am reușit să facem acest lucru deoarece tipul ServeMux are și o metodăServeHTTP, ceea ce înseamnă că satisface și interfața Handler.

pentru mine simplifică lucrurile să se gândească la un ServeMux ca fiind doar un tip special de handler, care în loc de a oferi un răspuns în sine trece cererea pe la un handler al doilea. Acest lucru nu este la fel de mult de un salt ca suna prima – înlănțuirea stivuitoare împreună este destul de banal în Go.

Handlere personalizate

să creăm un handler personalizat care să răspundă cu ora locală curentă într-un format dat:

type timeHandler struct { format string}func (th *timeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(th.format) w.Write(byte("The time is: " + tm))}

codul exact aici nu este prea important.

tot ce contează cu adevărat este că avem un obiect (în acest caz este untimeHandler struct, dar ar putea fi în egală măsură un șir sau o funcție sau orice altceva) și am implementat o metodă cu semnăturaServeHTTP(http.ResponseWriter, *http.Request) pe el. Asta e tot ce avem nevoie pentru a face un handler.

să încorporăm acest lucru într-un exemplu concret:

Fișier: principal.go

package mainimport ( "log" "net/http" "time")type timeHandler struct { format string}func (th *timeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(th.format) w.Write(byte("The time is: " + tm))}func main() { mux := http.NewServeMux() th := &timeHandler{format: time.RFC1123} mux.Handle("/time", th) log.Println("Listening...") http.ListenAndServe(":3000", mux)}

în main am inițializat timeHandler în exact același mod în care am face orice struct normal, folosind funcția

&

simbol pentru a obține un pointer. Și apoi, ca și exemplul anterior, folosim funcțiamux.Handlepentru a înregistra acest lucru la ServeMux.

acum, când vom rula aplicația, ServeMux va trece orice cerere pentru/time direct pe nostrutimeHandler.ServeHTTP metodă.

dă-i drumul și dă-i o încercare: http://localhost:3000/time.

observați că am putea reutiliza cu ușurință timeHandler în mai multe rute:

func main() { mux := http.NewServeMux() th1123 := &timeHandler{format: time.RFC1123} mux.Handle("/time/rfc1123", th1123) th3339 := &timeHandler{format: time.RFC3339} mux.Handle("/time/rfc3339", th3339) log.Println("Listening...") http.ListenAndServe(":3000", mux)}

funcționează ca stivuitoare

pentru cazuri simple (cum ar fi exemplul de mai sus) definirea de noi tipuri personalizate și metode servehttp se simte un pic verbose. Să ne uităm la o abordare alternativă, unde folosim Tipul http.HandlerFunc al lui Go pentru a constrânge o funcție normală să satisfacă interfața Handler.

orice funcție care are semnăturafunc(http.ResponseWriter, *http.Request) poate fi transformată într-un tip HandlerFunc. Acest lucru este util deoarece obiectele HandleFunc vin cu o metodă încorporată ServeHTTP care – destul de inteligent și convenabil – execută conținutul funcției originale.

dacă sună confuz, încercați să aruncați o privire la codul sursă relevant. Veți vedea că este un mod foarte succint de a face o funcție satisface interfața Handler.

să reproducem aplicația timeHandler folosind această tehnică:

Fișier: principal.go

package mainimport ( "log" "net/http" "time")func timeHandler(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(time.RFC1123) w.Write(byte("The time is: " + tm))}func main() { mux := http.NewServeMux() // Convert the timeHandler function to a HandlerFunc type th := http.HandlerFunc(timeHandler) // And add it to the ServeMux mux.Handle("/time", th) log.Println("Listening...") http.ListenAndServe(":3000", mux)}

de fapt, conversia unei funcții într-un tip HandlerFunc și apoi adăugarea acesteia la un ServeMux ca acesta este atât de comună încât Go oferă o comandă rapidă: metoda mux.HandleFunc.

așa ar fi arătat funcția main() dacă am fi folosit această scurtătură în schimb:

func main() { mux := http.NewServeMux() mux.HandleFunc("/time", timeHandler) log.Println("Listening...") http.ListenAndServe(":3000", mux)}

de cele mai multe ori folosind o funcția ca un handler ca acest lucru funcționează bine. Dar există un pic de limitare atunci când lucrurile încep să devină mai complexe.

probabil ați observat că, spre deosebire de metoda anterioară, a trebuit să codificăm formatul de timp în funcțiatimeHandler. Ce se întâmplă atunci când dorim să transmitem informații sau variabile de la main() unui handler?

o abordare curată este de a pune logica noastră de manipulare într-o închidere și de a închide variabilele pe care dorim să le folosim:

File: main.go

package mainimport ( "log" "net/http" "time")func timeHandler(format string) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(format) w.Write(byte("The time is: " + tm)) } return http.HandlerFunc(fn)}func main() { mux := http.NewServeMux() th := timeHandler(time.RFC1123) mux.Handle("/time", th) log.Println("Listening...") http.ListenAndServe(":3000", mux)}

funcția timeHandler are acum un rol subtil diferit. În loc să forțăm funcția într-un handler (așa cum am făcut-o anterior), acum o folosim pentru a returna un handler. Există două elemente cheie pentru a face acest lucru.

Mai întâi creeazăfn, o funcție anonimă care accesează‐ sau închide peste – variabilaformat formând o închidere. Indiferent de ceea ce facem cu închiderea, acesta va putea accesa întotdeauna variabilele locale în domeniul în care a fost creat – ceea ce înseamnă că în acest caz va avea întotdeauna acces la variabila format.

în al doilea rând, închiderea noastră are semnăturafunc(http.ResponseWriter, *http.Request). După cum vă amintiți mai devreme, acest lucru înseamnă că îl putem converti într-un tip HandlerFunc (astfel încât să satisfacă interfața Handler). Funcția noastră timeHandler returnează această închidere convertită.

în acest exemplu am fost doar trece un șir simplu la un handler. Dar într-o aplicație din lumea reală, puteți utiliza această metodă pentru a trece conexiunea bazei de date, harta șablonului sau orice alt context la nivel de aplicație. Este o alternativă bună la utilizarea variabilelor globale și are avantajul suplimentar de a face manipulatori autonomi pentru testare.

s-ar putea vedea, de asemenea, același model scris ca:

func timeHandler(format string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(format) w.Write(byte("The time is: " + tm)) })}

sau folosind o conversie implicită la tipul HandlerFunc la întoarcere:

func timeHandler(format string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(format) w.Write(byte("The time is: " + tm)) }}

DefaultServeMux

probabil ați văzut DefaultServeMux menționat în multe locuri, de la cele mai simple exemple Hello World la codul sursă Go.

mi-a luat mult timp să realizez că nu este nimic special. DefaultServeMux este doar un simplu ol’ ServeMux ca am fost deja folosind, care devine instanțiată în mod implicit atunci când este utilizat pachetul HTTP. Iată linia relevantă din sursa Go:

var DefaultServeMux = NewServeMux()

În general, nu ar trebui să utilizați DefaultServeMux deoarece prezintă un risc de securitate.deoarece DefaultServeMux este stocat într – o variabilă globală, orice pachet este capabil să-l acceseze și să înregistreze o rută-inclusiv orice pachete terțe pe care aplicația dvs. le importă. Dacă unul dintre aceste pachete terță parte este compromis, acestea ar putea utiliza DefaultServeMux pentru a expune un handler rău intenționat pe web.

deci, ca regulă generală, este o idee bună să evitați DefaultServeMux și, în schimb, să folosiți propriul ServeMux cu scop local, așa cum am fost până acum. Dar dacă ați decis să-l utilizați…

pachetul HTTP oferă câteva comenzi rapide pentru lucrul cu DefaultServeMux: http.Mâner și http.HandleFunc. Acestea fac exact același lucru cu funcțiile lor omonime pe care le-am analizat deja, cu diferența că adaugă Handlere la DefaultServeMux în loc de unul pe care l-ați creat.

în plus, ListenAndServe va reveni la utilizarea DefaultServeMux dacă nu este furnizat niciun alt handler (adică al doilea parametru este setat lanil).

deci, ca ultim pas, să actualizăm aplicația noastră timeHandler pentru a utiliza DefaultServeMux în schimb:

Fișier: principal.go

package mainimport ( "log" "net/http" "time")func timeHandler(format string) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { tm := time.Now().Format(format) w.Write(byte("The time is: " + tm)) } return http.HandlerFunc(fn)}func main() { // Note that we skip creating the ServeMux... var format string = time.RFC1123 th := timeHandler(format) // We use http.Handle instead of mux.Handle... http.Handle("/time", th) log.Println("Listening...") // And pass nil as the handler to ListenAndServe. http.ListenAndServe(":3000", nil)}

Dacă v-a plăcut această postare pe blog, nu uitați să consultați noua mea carte despre cum să construiți aplicații web profesionale cu Go!

Urmați-mă pe Twitter @ajmedwards.

toate fragmentele de cod din acest post pot fi utilizate gratuit sub licența MIT.



Lasă un răspuns

Adresa ta de email nu va fi publicată.