En Oppsummering Av Forespørselshåndtering I Go

Sist oppdatert: 21. januar 2018 Arkivert under: golang tutorial

Behandling AV HTTP-forespørsler med Go handler først og fremst om to ting: ServeMuxes og Handlers.

En ServeMux er i hovedsak EN HTTP request router (eller multiplexor). Den sammenligner innkommende forespørsler mot en liste over forhåndsdefinerte url-baner, og kaller den tilknyttede behandleren for banen når en kamp er funnet.

Handlers er ansvarlig for å skrive svar overskrifter og organer. Nesten ethvert objekt kan være en handler, så lenge det tilfredsstiller http.Handler – grensesnittet. I lay-termer betyr det ganske enkelt at Det må ha en ServeHTTP metode med følgende signatur:

ServeHTTP(http.ResponseWriter, *http.Request)GOS HTTP-pakke leveres med noen få funksjoner for å generere vanlige behandlere, for eksempelFileServerNotFoundHandlerogRedirectHandler. La oss begynne med et enkelt, men konstruert eksempel:

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

Fil: hoved.gå

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

La oss gå gjennom dette raskt:

  • I main – funksjonen bruker vi http.NewServeMux – funksjonen slik oppretter du en tom servemux.
  • vi bruker dahttp.RedirectHandler funksjonen for å opprette en ny handler. Denne behandleren 307 omdirigerer alle forespørsler den mottar til http://example.org.
  • neste bruker vimux.Handle funksjonen for å registrere dette med vår Nye ServeMux, så det fungerer som håndterer for alle innkommende forespørsler med URL-banen /foo.
  • Endelig oppretter vi en ny server og begynne å lytte for innkommende forespørsler medhttp.ListenAndServe funksjon, passerer i Vår ServeMux for det å matche forespørsler mot.

gå videre og kjør programmet:

$ go run main.goListening...

og besøkhttp://localhost:3000/foo i nettleseren din. Du bør finne at forespørselen blir omdirigert.

eagle-eyed av dere har kanskje lagt merke til noe interessant: signaturen for ListenAndServe-funksjonen er ListenAndServe(addr string, handler Handler), men Vi passerte En ServeMux som den andre parameteren.

Vi kunne gjøre dette fordi ServeMux-typen også har enServeHTTP metode, noe som betyr at Den også tilfredsstiller Håndterings grensesnittet.

for meg forenkler det ting å tenke På En ServeMux som bare å være en spesiell type handler, som i stedet for å gi et svar selv sender forespørselen videre til en annen handler. Dette er ikke så mye av et sprang som det først høres ut-kjedehåndterere sammen er ganske vanlig I Go.

Tilpassede Behandlere

la oss lage en tilpasset behandlere som svarer med gjeldende lokal tid i et gitt format:

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

den nøyaktige koden her er ikke så viktig.

Alt som virkelig betyr noe er at vi har et objekt (i dette tilfellet er det en timeHandler struct, men det kan også være en streng eller funksjon eller noe annet), og vi har implementert en metode med signaturen ServeHTTP(http.ResponseWriter, *http.Request) på den. Det er alt vi trenger for å lage en handler.

la oss legge inn dette i et konkret eksempel:

Fil: main. gå

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

i main – funksjonen initialiserte vi timeHandler på nøyaktig samme måte som vi ville gjort med en hvilken som helst normal struktur ved å bruke & symbol for å gi en peker. Og så, som forrige eksempel, bruker vi mux.Handle – funksjonen for å registrere dette med Vår ServeMux.

Nå når vi kjører programmet, Vil ServeMux passere enhver forespørsel om /time rett til vår timeHandler.ServeHTTP metode.

Gå videre og gi det et forsøk: http://localhost:3000/time.

Legg Også Merke til at vi lett kunne gjenbruke timeHandler i flere ruter:

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

fungerer som behandlere

for enkle tilfeller (som eksempelet ovenfor) definere nye tilpassede typer og servehttp metoder føles litt detaljert. La oss se på en alternativ tilnærming, hvor Vi utnytter Go ‘s http.HandlerFunc type for å tvinge en normal funksjon til å tilfredsstille Behandlings-grensesnittet.

enhver funksjon som har signaturen func(http.ResponseWriter, *http.Request) kan konverteres til En HandlerFunc-type. Dette er nyttig fordi HandleFunc-objekter kommer med en innebygd ServeHTTP metode som – ganske smart og praktisk-utfører innholdet i den opprinnelige funksjonen.

hvis det høres forvirrende ut, kan du prøve å ta en titt på den aktuelle kildekoden. Du vil se at det er en veldig kortfattet måte å gjøre en funksjon tilfredsstille Handler grensesnittet.

la oss reprodusere timeHandler-programmet ved hjelp av denne teknikken:

Fil: hoved.gå

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

faktisk konvertere en funksjon Til En HandlerFunc type og deretter legge den til En ServeMux som dette er så vanlig At Go gir en snarvei:mux.HandleFunc metoden.

dette er hva main() funksjonen ville ha sett ut hvis vi hadde brukt denne snarveien i stedet:

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

Mesteparten av tiden bruker en fungerer som en handler Som Dette Fungerer bra. Men det er litt av en begrensning når ting begynner å bli mer komplekse.

du har sikkert lagt merke til at, i motsetning til metoden før, har vi måttet hardcode tidsformatet itimeHandler – funksjonen. Hva skjer når vi vil sende informasjon eller variabler fra main() til en handler?

en fin tilnærming er å sette vår handler logikk inn i en lukning, og lukke over variablene vi vil bruke:

Fil: main.gå

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 funksjonen har nå en litt annen rolle. I stedet for å tvinge funksjonen til en handler (som vi gjorde tidligere), bruker vi nå den til å returnere en handler. Det er to viktige elementer for å gjøre dette arbeidet.

først oppretter den fn, en anonym funksjon som åpner‐ eller lukker over – format variabelen som danner en lukning. Uansett hva vi gjør med lukkingen, vil det alltid kunne få tilgang til variablene som er lokale til omfanget det ble opprettet i-som i dette tilfellet betyr at det alltid vil ha tilgang til format variabelen.

For det andre har vår nedleggelse signaturen func(http.ResponseWriter, *http.Request). Som du kanskje husker fra tidligere, betyr dette at vi kan konvertere den til En HandlerFunc-type (slik at den tilfredsstiller Behandlings-grensesnittet). Vår timeHandler – funksjon returnerer deretter denne konverterte lukkingen.

i dette eksemplet har vi nettopp passert en enkel streng til en handler. Men i en reell søknad kan du bruke denne metoden til å passere databasetilkobling, malkart eller annen kontekst på applikasjonsnivå. Det er et godt alternativ til å bruke globale variabler, og har den ekstra fordelen av å lage ryddige selvstendige håndterere for testing.

du kan også se det samme mønsteret skrevet som:

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

Eller bruke en implisitt konvertering Til HandlerFunc-typen ved retur:

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

Du har sikkert sett DefaultServeMux nevnt mange steder, fra De enkleste Hello World-eksemplene til Go-kildekoden.

Det tok meg lang tid å innse at det ikke er noe spesielt. DefaultServeMux er bare en vanlig ol ‘ ServeMux som vi allerede har brukt, som blir instantiert som standard når HTTP-pakken brukes. Her er den relevante linjen Fra Go-kilden:

var DefaultServeMux = NewServeMux()

generelt bør Du ikke bruke DefaultServeMux fordi Det utgjør en sikkerhetsrisiko.

Fordi DefaultServeMux er lagret i en global variabel, kan enhver pakke få tilgang til den og registrere en rute-inkludert eventuelle tredjepartspakker som programmet importerer. Hvis en av disse tredjepartspakkene er kompromittert, kan De bruke DefaultServeMux til å avsløre en ondsinnet behandler på nettet.

så som en tommelfingerregel er det en god ide å unngå DefaultServeMux, og i stedet bruke din egen lokalt scoped ServeMux, som vi har vært så langt. Men hvis du bestemte deg for å bruke den…

HTTP-pakken inneholder et par snarveier for å jobbe Med DefaultServeMux: http. Håndtak og http.HandleFunc. Disse gjør akkurat det samme som deres navnefunksjoner vi allerede har sett på, med forskjellen at de legger til behandlere Til DefaultServeMux i stedet for en du har opprettet.

I Tillegg Vil ListenAndServe falle tilbake til Å bruke DefaultServeMux hvis ingen annen behandler er oppgitt (det vil si at den andre parameteren er satt til nil).

så som et siste trinn, la oss oppdatere vår timeHandler program for Å bruke DefaultServeMux stedet:

Fil: main.gå

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

hvis du likte dette blogginnlegget, ikke glem å sjekke ut min nye bok om hvordan du bygger profesjonelle webapplikasjoner med Go!

Følg Meg på twitter @ajmedwards.

alle kodebiter i dette innlegget er gratis å bruke under Mit-Lisensen.



Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert.