o recapitulare a solicitării de manipulare în Go
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 FileServer
NotFoundHandler
și RedirectHandler
. Să începem cu un exemplu simplu, dar inventat:
să trecem prin asta repede:
- În
main
funcția folosimhttp.NewServeMux
funcția pentru a crea un servemux gol. - apoi folosim
http.RedirectHandler
funcția pentru a crea un nou handler. Acest handler 307 redirecționează toate cererile pe care le primește lahttp://example.org
. - în continuare vom folosi
mux.Handle
funcț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ția
http.ListenAndServe
, trecând în ServeMux pentru ca acesta să se potrivească cererilor.
mergeți mai departe și rulați aplicația:
ș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:
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:
î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.Handle
pentru 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ț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ă:
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:
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:
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:
sau folosind o conversie implicită la tipul HandlerFunc la întoarcere:
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:
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.