Rekapitulace Žádost Manipulaci v

Naposledy aktualizováno: 21. ledna 2018 soubor pod: golang tutorial

Zpracování HTTP požadavků s Go je především o dvě věci: ServeMuxes a Manipulátory.

ServeMux je v podstatě HTTP požadavek router (nebo multiplexor). Porovnává příchozí požadavky se seznamem předdefinovaných cest URL a volá přidruženou obslužnou rutinu pro cestu, kdykoli je nalezena shoda.

manipulátory jsou zodpovědné za zápis hlaviček a těl odpovědí. Obsluhou může být téměř jakýkoli objekt, pokud splňuje rozhraní http.Handler. V laických termínech, to jednoduše znamená, že musí mít ServeHTTP metoda s následující podpis:

ServeHTTP(http.ResponseWriter, *http.Request)

Go to HTTP balíček lodě s několika funkcemi generovat běžné rutiny, jako je FileServerNotFoundHandlerRedirectHandler. Začněme jednoduchým, ale vymyšleným příkladem:

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

File: main.jít

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

Pojďme se krok přes to rychle:

  • V main funkce používáme http.NewServeMux funkce pro vytvoření prázdné ServeMux.
  • potom použijeme funkci http.RedirectHandler k vytvoření nové obslužné rutiny. Tento obslužný program 307 přesměruje všechny požadavky, které obdrží, na http://example.org.
  • dále jsme pomocí mux.Handle funkce zaregistrovat s naší novou ServeMux, tak to působí jako handler pro všechny příchozí požadavky s cesty URL /foo.
  • nakonec vytvoříme nový server a začneme poslouchat příchozí požadavky pomocí funkce http.ListenAndServe a předáme v našem Servemuxu, aby odpovídal požadavkům.

nestyď se a spusťte aplikaci:

$ go run main.goListening...

navštívit http://localhost:3000/foo ve vašem prohlížeči. Měli byste zjistit, že váš požadavek bude úspěšně přesměrován.

eagle-eyed z vás si možná všimli něčeho zajímavého: podpis ListenAndServe funkce je ListenAndServe(addr string, handler Handler), ale prošli jsme kolem ServeMux jako druhý parametr.

byli Jsme schopni to udělat, protože ServeMux typ má také ServeHTTP metoda, což znamená, že to taky splňuje Handler rozhraní.

Pro mě to zjednodušuje věci, že ServeMux, jak jen je zvláštní druh handler, který místo toho poskytuje odpověď sám, předá žádost k druhé handler. To není tak velký skok, jak to nejprve zní-řetězení manipulátorů dohromady je v Go docela běžné.

Vlastní obslužné Rutiny

Pojďme vytvořit vlastní handler, který reaguje s aktuální místní čas v daném formátu:

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

přesný kód zde není příliš důležité.

Vše, co je skutečně důležité, je, že máme objekt (v tomto případě jde o timeHandler struct, ale stejně tak může být řetězec nebo funkce, nebo cokoliv jiného), a jsme implementovali metodu s podpisem ServeHTTP(http.ResponseWriter, *http.Request). To je vše, co potřebujeme k tomu, abychom vytvořili psovoda.

vložme to do konkrétního příkladu:

File: main.jít

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

V main funkce my inicializován timeHandler přesně stejným způsobem, jako bychom každý normální struct, pomocí & symbol výnos ukazatel. A pak, stejně jako předchozí příklad, použijeme funkci mux.Handle k registraci na našem Servemuxu.

Nyní, když spustíme aplikaci, ServeMux předá jakýkoli požadavek na /time přímo na naši metodu timeHandler.ServeHTTP.

jděte do toho a zkuste to: http://localhost:3000/time.

Všimněte si taky, že bychom mohli snadno znovu použít timeHandler v několika trasách:

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

Funkce jako ovladače

Pro jednoduché případy (jako v příkladu výše) definování nových uživatelských typů a ServeHTTP metody je trochu upovídaný. Pojďme se podívat na alternativní přístup, kde jsme využít Go http.HandlerFunc typ přimět k normální funkci do splňující Handler rozhraní.

jakákoliv funkce s podpisem func(http.ResponseWriter, *http.Request) může být převedena na typ HandlerFunc. To je užitečné, protože objekty HandleFunc přicházejí s vestavěnou metodou ServeHTTP, která-poměrně chytře a pohodlně-provádí obsah původní funkce.

pokud to zní matoucí, zkuste se podívat na příslušný zdrojový kód. Uvidíte, že je to velmi stručný způsob, jak vytvořit funkci uspokojující rozhraní obslužného programu.

pojďme reprodukovat aplikaci timeHandler pomocí této techniky:

File: main.jít

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

Ve skutečnosti, konverzi funkce do HandlerFunc typ a pak přidáním do ServeMux jako je to tak běžné, že Jdou poskytuje zkratku: mux.HandleFunc metoda.

Toto je main() funkce by vypadala jako, pokud bychom použili tuto zkratku namísto:

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

Většinu času pomocí funkce jako psovod, jako to funguje dobře. Ale tam je trochu omezení, když se věci začnou stále složitější.

pravděpodobně Jste si všimli, že, na rozdíl od metody dříve, než jsme museli hardcode formát času v timeHandler funkce. Co se stane, když chceme předat informace nebo proměnné z main() handler?

elegantní přístup je, aby naše handler logiky do uzavření, zavřít a proměnné, které chceme používat:

File: main.jít

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

timeHandler funkce nyní má jemně rozdílných role. Místo donucování funkce do handler (jako jsme to udělali dříve), nyní používat to, aby návrat handler. K tomu, aby to fungovalo, jsou dva klíčové prvky.

Nejprve se vytvoří fn, anonymní funkce, která přistupuje &dash, nebo uzavře – format proměnné tvoří uzávěr. Bez ohledu na to, co děláme s uzavřením, bude mít vždy přístup k proměnným, které jsou lokální k rozsahu, ve kterém byly vytvořeny – což v tomto případě znamená, že bude mít vždy přístup k proměnné format.

za druhé naše uzavření má podpis func(http.ResponseWriter, *http.Request). Jak si možná pamatujete z dříve, to znamená, že jej můžeme převést na typ HandlerFunc (tak, aby vyhovoval rozhraní Handler). Naše funkce timeHandler pak vrátí toto převedené uzavření.

v tomto příkladu jsme právě předávali jednoduchý řetězec handlerovi. Ale v aplikaci v reálném světě můžete tuto metodu použít k předání připojení k databázi, mapy šablon nebo jakéhokoli jiného kontextu na úrovni aplikace. Je to dobrá alternativa k používání globálních proměnných a má další výhodu v tom, že vytváří čisté samostatné manipulátory pro testování.

můžete také vidět stejný vzor zapsat jako:

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

Nebo pomocí implicitní konverze na HandlerFunc typ na návrat:

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

pravděpodobně Jste to viděli DefaultServeMux zmínil, v mnoha místech, a to od nejjednodušších Hello World příkladů na Cestách zdrojový kód.

trvalo mi dlouho, než jsem si uvědomil, že to není nic zvláštního. DefaultServeMux je jen obyčejný ol ‚ ServeMux, jako jsme již používali, který dostane instance ve výchozím nastavení při použití balíčku HTTP. Zde je příslušný řádek ze zdroje Go:

var DefaultServeMux = NewServeMux()

obecně byste defaultservemux neměli používat, protože představuje bezpečnostní riziko.

protože je DefaultServeMux uložen v globální proměnné, může k němu přistupovat jakýkoli balíček a zaregistrovat trasu-včetně všech balíčků třetích stran, které vaše aplikace importuje. Pokud jeden z těch balíků třetích stran je ohrožena, mohou použít DefaultServeMux vystavit škodlivý handler na web.

takže zpravidla je dobré vyhnout se Defaultservemuxu a místo toho použít svůj vlastní lokálně zaměřený ServeMux, jako jsme byli dosud. Ale pokud jste se rozhodli ji použít…

HTTP balíček poskytuje několik zkratek pro práci s DefaultServeMux: http.Rukojeť a http.HandleFunc. Tyto přesně stejné, jako jejich jmenovci funkcí jsme se již podívali, s tím rozdílem, že oni přidat obslužné rutiny pro DefaultServeMux namísto jednoho, který jste vytvořili.

Navíc, ListenAndServe bude klesat zpět k použití DefaultServeMux pokud žádný jiný handler je k dispozici (to je, druhý parametr je nastaven na nil).

jako poslední krok, pojďme aktualizovat své timeHandler aplikace používat DefaultServeMux místo:

File: main.jít

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

Pokud se vám to líbilo tento blog post, nezapomeňte se podívat na mé nové knize o tom, jak vytvořit profesionální webové aplikace s Go!

Následujte mě na Twitteru @ajmedwards.

všechny úryvky kódu v tomto příspěvku jsou zdarma k použití pod licencí MIT.



Napsat komentář

Vaše e-mailová adresa nebude zveřejněna.