a Recap of Request Handling in Go

Last updated: 21st January 2018 Filed under: golang tutorial

Processing HTTP requests with Go is primarely about two things: ServeMuxes and Handlers.

ServeMux on pohjimmiltaan HTTP-pyyntöreititin (tai multiplexor). Se vertaa saapuvia pyyntöjä ennalta määritettyjen URL-polkujen luetteloon ja kutsuu polun käsittelijää aina, kun osuma löytyy.

käsittelijät vastaavat vastausten otsikoiden ja elinten kirjoittamisesta. Lähes mikä tahansa objekti voi olla käsittelijä, kunhan se täyttää http.Handler rajapinnan. Maallikkotermein tämä tarkoittaa yksinkertaisesti sitä, että sillä on oltava ServeHTTP menetelmä, jossa on seuraava allekirjoitus:

ServeHTTP(http.ResponseWriter, *http.Request)

Go: n HTTP-pakettialukset, joissa on muutama toiminto yhteisten käsittelijöiden tuottamiseksi, kuten FileServerNotFoundHandler ja RedirectHandler. Aloitetaan yksinkertaisella mutta keksityllä esimerkillä:

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

File: main.go

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

mennään tästä nopeasti läpi:

  • main funktio käytämme http.NewServeMux funktio luoda tyhjä servemux.
  • tämän jälkeen käytetään http.RedirectHandler funktiota uuden käsittelijän luomiseen. Käsittelijä 307 ohjaa kaikki vastaanottamansa pyynnöt osoitteeseen http://example.org.
  • seuraavaksi käytämme mux.Handle funktiota tämän rekisteröimiseksi uudella ServeMux ’ llämme, joten se toimii kaikkien saapuvien pyyntöjen käsittelijänä URL-polulla /foo.
  • lopuksi luomme uuden palvelimen ja alamme kuunnella saapuvia pyyntöjä http.ListenAndServe funktiolla, siirtäen Servemuximme sen sovittamaan pyyntöjä vastaan.

Käynnistä sovellus:

$ go run main.goListening...

ja käy http://localhost:3000/foo selaimessasi. Sinun pitäisi huomata, että pyyntösi saa onnistuneesti ohjataan.

tarkkasilmäiset saattoivat huomata jotain mielenkiintoista: listenandserve-funktion tunnus on ListenAndServe(addr string, handler Handler), mutta ohitimme toisena parametrina Servemuxin.

tähän pystyimme, koska ServeMux-tyypillä on myös ServeHTTP – menetelmä, eli sekin täyttää käsittelijän rajapinnan.

minulle on yksinkertaistavaa ajatella, että ServeMux on vain erityinen käsittelijä, joka itse vastauksen antamisen sijaan siirtää pyynnön toiselle käsittelijälle. Tämä ei ole niin paljon harppaus kuin se ensin kuulostaa – ketjuttaminen käsittelijät yhdessä on melko yleistä Go.

Custom Handlers

luodaan mukautettu käsittelijä, joka vastaa kulloiseenkin paikalliseen aikaan tietyssä muodossa:

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

tarkka koodi tässä ei ole liian tärkeä.

tärkeintä on, että meillä on objekti (tässä tapauksessa se on timeHandler strukturoitu, mutta se voisi yhtä hyvin olla merkkijono tai funktio tai mikä tahansa muu), ja olemme toteuttaneet menetelmän, jossa on allekirjoitus ServeHTTP(http.ResponseWriter, *http.Request) siihen. Se riittää käsittelijäksi.

upotetaan tämä konkreettisena esimerkkinä:

File: main.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)}

in the main function we initialized the timeHandler in tismalleen samalla tavalla we would any normal structured, using the & symboli osoittimen antamiseksi. Ja sitten, kuten edellisessä esimerkissä, käytämme mux.Handle funktiota rekisteröimään tämän ServeMux ’ llemme.

nyt kun suoritamme sovelluksen, ServeMux välittää kaikki /time suoraan meidän timeHandler.ServeHTTP – menetelmällemme.

Go ahead and give it a try: http://localhost:3000/time.

Huomaa myös, että timeHandler usealla reitillä:

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

toimii käsittelijöinä

yksinkertaisissa tapauksissa (kuten yllä olevassa esimerkissä), joissa määritellään uusia mukautettuja tyyppejä ja servehttp: / / www-menetelmät tuntuvat hieman monisanaisilta. Tarkastellaan vaihtoehtoista lähestymistapaa, jossa hyödynnämme Go: n http.HandlerFunc – tyyppiä pakottaaksemme normaalin funktion täyttämään käsittelijän rajapinnan.

mikä tahansa funktio, jolla on allekirjoitus func(http.ResponseWriter, *http.Request), voidaan muuntaa HandlerFunc-tyypiksi. Tämä on hyödyllistä, koska HandleFunc – objektien mukana tulee sisäänrakennettu ServeHTTP menetelmä, joka – melko taitavasti ja kätevästi-suorittaa alkuperäisen funktion sisällön.

Jos tämä kuulostaa hämmentävältä, kokeile vilkaista asiaankuuluvaa lähdekoodia. Näet, että se on hyvin ytimekäs tapa tehdä toiminto tyydyttää käsittelijä käyttöliittymä.

toistetaan timeHandler-sovellus tällä tekniikalla:

File: main.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)}

itse asiassa funktion muuntaminen Käsittelijäfunc-tyyppiseksi ja sen lisääminen tämän kaltaiseen Servemuxiin on niin yleistä, että Go tarjoaa oikotien: mux.HandleFunc menetelmä.

tältä main() funktio olisi näyttänyt, jos olisimme käyttäneet sen sijaan tätä oikotietä:

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

suurimman osan ajasta käyttäen tällainen toiminta käsittelijänä toimii hyvin. Mutta siinä on vähän rajoitusta, kun asiat alkavat monimutkaistua.

olet luultavasti huomannut, että aiemmasta menetelmästä poiketen meidän on pitänyt kovettaa aikamuoto timeHandler funktiossa. Mitä tapahtuu, kun haluamme välittää tietoa tai muuttujia main() käsittelijälle?

siisti lähestymistapa on laittaa käsittelijälogiikkamme lukkoon ja sulkea käyttämämme muuttujat:

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

timeHandler funktiolla on nyt hienovaraisesti erilainen rooli. Sen sijaan, että pakottaisimme toiminnon käsittelijäksi (kuten teimme aiemmin), käytämme sitä nyt palauttaaksemme käsittelijän. Tässä on kaksi keskeistä tekijää.

ensin se Luo fn, anonyymin funktion, joka käyttää ‐ tai sulkee Overin – format muuttujan muodostaen sulkimen. Riippumatta siitä, mitä teemme sulkemisella, se pääsee aina käsiksi muuttujiin, jotka ovat paikallisia sille alueelle, jossa se luotiin – mikä tässä tapauksessa tarkoittaa, että sillä on aina pääsy format muuttuja.

toiseksi sulkemisessamme on allekirjoitus func(http.ResponseWriter, *http.Request). Kuten saatat muistaa aiemmin, tämä tarkoittaa, että voimme muuntaa sen HandlerFunc tyyppi (niin, että se täyttää Handler käyttöliittymä). Meidän timeHandler funktio palauttaa tämän muunnetun sulkemisen.

tässä esimerkissä olemme juuri siirtäneet yksinkertaisen merkkijonon käsittelijälle. Mutta reaalimaailman sovellus voisi käyttää tätä menetelmää siirtää tietokantayhteyden, mallikartan, tai minkä tahansa muun sovellustason yhteydessä. Se on hyvä vaihtoehto käyttää globaaleja muuttujia, ja on lisäetuna tehdä siisti itsenäinen käsittelijät testaukseen.

saman kuvion voi nähdä myös kirjoitettuna:

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

tai käyttämällä implisiittistä muuntamista HandlerFunc-tyyppiin paluun yhteydessä:

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

olet luultavasti nähnyt DefaultServeMux ’ n mainittavan monessa paikassa, yksinkertaisimmista Hello World-esimerkeistä Go: n lähdekoodiin.

kesti kauan tajuta, ettei siinä ole mitään erikoista. DefaultServeMux on vain tavallinen ol ’ ServeMux kuten olemme jo käyttäneet, joka saa instantiated oletuksena, kun HTTP-pakettia käytetään. Tässä asiaankuuluva rivi Go-lähteestä:

var DefaultServeMux = NewServeMux()

yleensä Defaultservemuxia ei kannata käyttää, koska se aiheuttaa tietoturvariskin.

koska DefaultServeMux on tallennettu kokonaismuuttujaan, mikä tahansa paketti voi käyttää sitä ja rekisteröidä reitin – mukaan lukien sovelluksesi tuomat kolmannen osapuolen paketit. Jos jokin näistä kolmannen osapuolen paketeista vaarantuu, he voivat käyttää DefaultServeMux-toimintoa haitallisen käsittelijän paljastamiseksi verkkoon.

nyrkkisääntönä on siis hyvä välttää DefaultServeMux, ja käyttää sen sijaan omaa paikallisesti scopattua Servemuxia, kuten tähänkin asti. Mutta jos päätät käyttää sitä…

HTTP-paketti tarjoaa pari pikakuvaketta, joilla voit työskennellä DefaultServeMux: http-komennon kanssa.Käsittele ja http.HandleFunc. Nämä tekevät täsmälleen samoin kuin niiden kaimatoiminnot, joita olemme jo tarkastelleet, sillä erotuksella, että ne lisäävät käsittelijöitä Defaultservemuxiin luomasi sijaan.

lisäksi ListenAndServe sortuu käyttämään Defaultservemuxia, jos muita käsittelijöitä ei ole (eli toinen parametri on asetettu nil).

joten viimeisenä vaiheena päivitetäänpä timeHandler-sovelluksemme käyttämään Defaultservemuxia sen sijaan:

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() { // 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)}

Jos pidit tästä blogikirjoituksesta, älä unohda tarkistaa uutta Kirjaani siitä, miten ammattimaisia verkkosovelluksia Go!

Seuraa minua Twitterissä @ajmedwards.

kaikki tässä viestissä olevat koodinpätkät ovat vapaasti käytettävissä MIT-lisenssin nojalla.



Vastaa

Sähköpostiosoitettasi ei julkaista.