En sammanfattning av begäran hantering I Go
bearbetning HTTP-förfrågningar med Go handlar främst om två saker: ServeMuxes och hanterare.
en ServeMux är i huvudsak en HTTP – begäran router (eller multiplexor). Den jämför inkommande förfrågningar mot en lista med fördefinierade URL-sökvägar och anropar den associerade hanteraren för Sökvägen när en matchning hittas.
hanterare är ansvariga för att skriva svarhuvuden och organ. Nästan alla objekt kan vara en hanterare, så länge det uppfyller gränssnittet http.Handler
. I lay-termer betyder det helt enkelt att det måste ha en ServeHTTP
metod med följande signatur:
ServeHTTP(http.ResponseWriter, *http.Request)
Go: s HTTP-paket levereras med några funktioner för att generera vanliga hanterare, till exempel FileServer
NotFoundHandler
och RedirectHandler
. Låt oss börja med ett enkelt men konstruerat exempel:
låt oss gå igenom detta snabbt:
- i funktionen
main
vi använder funktionenhttp.NewServeMux
för att skapa en tom servemux. - vi använder sedan funktionen
http.RedirectHandler
för att skapa en ny hanterare. Denna hanterare 307 omdirigerar alla förfrågningar som den tar emot tillhttp://example.org
. - Nästa använder vi funktionen
mux.Handle
för att registrera detta med vår nya ServeMux, så den fungerar som Hanterare för alla inkommande förfrågningar med URL-sökvägen/foo
. - slutligen skapar vi en ny server och börjar lyssna på inkommande förfrågningar med funktionen
http.ListenAndServe
, som passerar i vår ServeMux för att den ska matcha förfrågningar mot.
gå vidare och kör programmet:
och besök http://localhost:3000/foo
I din webbläsare. Du bör finna att din begäran blir framgångsrikt omdirigeras.du kanske har märkt något intressant: signaturen för ListenAndServe-funktionen är ListenAndServe(addr string, handler Handler)
, men vi passerade en ServeMux som den andra parametern.
vi kunde göra detta eftersom ServeMux-typen också har enServeHTTP
– metod, vilket innebär att den också uppfyller hanteringsgränssnittet.
för mig förenklar det saker att tänka på en ServeMux som bara en speciell typ av hanterare, som istället för att ge ett svar själv skickar begäran till en andra hanterare. Det här är inte så mycket av ett steg som det först låter – kedjehanterare tillsammans är ganska vanligt I Go.
anpassade hanterare
Låt oss skapa en anpassad hanterare som svarar med aktuell lokal tid i ett givet format:
den exakta koden här är inte så viktig.
allt som verkligen betyder något är att vi har ett objekt (i det här fallet är det ett timeHandler
struct, men det kan också vara en sträng eller funktion eller något annat), och vi har implementerat en metod med signaturen ServeHTTP(http.ResponseWriter, *http.Request)
på den. Det är allt vi behöver för att göra en handler.
låt oss bädda in detta i ett konkret exempel:
i funktionen main
vi initierade timeHandler
på exakt samma sätt som vi skulle någon normal struktur, med hjälp av &
symbol för att ge en pekare. Och sedan, som föregående exempel, använder vi funktionen mux.Handle
för att registrera detta med vår ServeMux.
Nu när vi kör programmet kommer ServeMux att skicka någon begäran om/time
direkt till vårtimeHandler.ServeHTTP
metod.
gå vidare och ge det ett försök: http://localhost:3000/time
.
Lägg märke till att vi enkelt kan återanvända timeHandler
på flera rutter:
fungerar som hanterare
för enkla fall (som exemplet ovan) definierar nya anpassade typer och servehttp metoder känns lite utförlig. Låt oss titta på ett alternativt tillvägagångssätt, där vi utnyttjar Go: s http.HandlerFunc
typ för att tvinga en normal funktion till att uppfylla hanteringsgränssnittet.
alla funktioner som har signaturen func(http.ResponseWriter, *http.Request)
kan konverteras till en HandlerFunc-typ. Detta är användbart eftersom HandleFunc-objekt levereras med en inbyggd ServeHTTP
metod som – ganska smart och bekvämt – utför innehållet i den ursprungliga funktionen.
om det låter förvirrande, försök ta en titt på relevant källkod. Du ser att det är ett mycket kortfattat sätt att få en funktion att tillfredsställa hanterarens gränssnitt.
låt oss reproducera timeHandler-applikationen med den här tekniken:
faktum är att konvertera en funktion till en HandlerFunc-typ och sedan lägga till den i en ServeMux så här är så vanligt att Go ger en genväg: metoden mux.HandleFunc
.
det här är vad funktionen main()
skulle ha sett ut om vi hade använt den här genvägen istället:
För det mesta med en fungerar som en hanterare som detta fungerar bra. Men det är lite av en begränsning när saker börjar bli mer komplexa.
Du har säkert märkt att vi, till skillnad från metoden tidigare, måste hårdkoda tidsformatet i funktionen timeHandler
. Vad händer när vi vill skicka information eller variabler från main()
till en hanterare?
ett snyggt tillvägagångssätt är att sätta vår hanteringslogik i en stängning och stänga över de variabler vi vill använda:
funktionen timeHandler
har nu en subtilt annorlunda roll. Istället för att tvinga funktionen till en hanterare (som vi gjorde tidigare) använder vi den nu för att returnera en hanterare. Det finns två viktiga element för att göra detta arbete.
först skapar fn
, en anonym funktion som kommer åt &streck; eller stänger över – format
variabel som bildar en stängning. Oavsett vad vi gör med stängningen kommer det alltid att kunna komma åt variablerna som är lokala till det omfång det skapades i – vilket i det här fallet betyder att det alltid har tillgång till format
variabel.
För det andra har vår stängning signaturen func(http.ResponseWriter, *http.Request)
. Som du kanske kommer ihåg från tidigare betyder det att vi kan konvertera den till en Hanterfunc-typ (så att den uppfyller hanterarens gränssnitt). Vår timeHandler
– funktion returnerar sedan denna konverterade stängning.
i det här exemplet har vi bara överfört en enkel sträng till en hanterare. Men i en verklig applikation kan du använda den här metoden för att skicka databasanslutning, mallkarta eller något annat programkontext. Det är ett bra alternativ till att använda globala variabler, och har den extra fördelen att göra snygga fristående Hanterare för testning.
Du kan också se samma mönster skrivet som:
eller använda en implicit konvertering till typen HandlerFunc vid retur:
DefaultServeMux
Du har förmodligen sett DefaultServeMux nämns på många ställen, från de enklaste Hello World-exemplen till go-källkoden.
det tog mig lång tid att inse att det inte är något speciellt. DefaultServeMux är bara en vanlig ol ’ ServeMux som vi redan har använt, vilket blir instansierat som standard när HTTP-paketet används. Här är den relevanta raden från Go-källan:
var DefaultServeMux = NewServeMux()
Generellt bör du inte använda DefaultServeMux eftersom det utgör en säkerhetsrisk.
eftersom DefaultServeMux lagras i en global variabel kan alla paket komma åt den och registrera en rutt – inklusive alla tredjepartspaket som din applikation importerar. Om ett av dessa tredjepartspaket äventyras kan de använda DefaultServeMux för att exponera en skadlig hanterare på webben.
så som en tumregel är det bra att undvika DefaultServeMux, och istället använda din egen lokalt scoped ServeMux, som vi har varit hittills. Men om du bestämde dig för att använda den…
HTTP-paketet innehåller ett par genvägar för att arbeta med DefaultServeMux: http.Hantera och http.HandleFunc. Dessa gör exakt samma som deras namnefunktioner som vi redan har tittat på, med skillnaden att de lägger till hanterare till DefaultServeMux istället för en som du har skapat.
Dessutom kommer ListenAndServe att falla tillbaka till att använda DefaultServeMux om ingen annan hanterare tillhandahålls (det vill säga den andra parametern är inställd på nil
).
så som ett sista steg, låt oss uppdatera vår timeHandler-applikation för att använda DefaultServeMux istället:
om du gillade det här blogginlägget, glöm inte att kolla in min nya bok om hur man bygger professionella webbapplikationer med Go!
Följ mig på Twitter @ajmedwards.
alla kodavsnitt i det här inlägget är gratis att använda under MIT-licensen.