a Recap kérelem kezelése Go

Utolsó frissítés: 21st január 2018 Kategória: golang tutorial

feldolgozása HTTP kérések Go elsősorban két dolog: ServeMuxes és kezelők.

a ServeMux lényegében egy HTTP kérés router (vagy multiplexor). Összehasonlítja a beérkező kéréseket az előre definiált URL-útvonalak listájával, és meghívja az elérési út társított kezelőjét, amikor egyezést talál.

a kezelők felelősek a válasz fejlécek és testek írásáért. Szinte minden objektum lehet kezelő, amennyiben megfelel a http.Handler felületnek. Laikus értelemben ez egyszerűen azt jelenti, hogy rendelkeznie kell egy ServeHTTP módszerrel a következő aláírással:

ServeHTTP(http.ResponseWriter, *http.Request)

A GO HTTP csomagja néhány funkcióval rendelkezik, hogy közös kezelőket generáljon, például FileServerNotFoundHandler és RedirectHandler. Kezdjük egy egyszerű, de kitalált példával:

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

Fájl: fő.menj

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

lépjünk át gyorsan ezen:

  • a main funkció a http.NewServeMux funkció ahhoz, hogy hozzon létre egy üres servemux.
  • ezután a http.RedirectHandler függvényt használjuk egy új kezelő létrehozásához. Ez a 307 kezelő átirányítja az összes beérkező kérést a http://example.orgcímre.
  • ezután a mux.Handle függvényt használjuk, hogy regisztráljuk ezt az új ServeMux-nál, így az összes bejövő kérés kezelőjeként működik az URL elérési útjával /foo.
  • végül létrehozunk egy új szervert, és elkezdjük hallgatni a bejövő kéréseket a http.ListenAndServe funkcióval, átadva a ServeMux-ot, hogy megfeleljen a kéréseknek.

menj előre, és futtassa az alkalmazást:

$ go run main.goListening...

és látogasson elhttp://localhost:3000/foo a böngészőben. Meg kell találni, hogy a kérés lesz sikeresen átirányítva.

a sasszemű lehet, hogy észrevett valami érdekeset: a ListenAndServe függvény aláírása ListenAndServe(addr string, handler Handler), de a ServeMux-ot adtuk át második paraméterként.

ezt azért tudtuk megtenni, mert a ServeMux típusnak van egy ServeHTTP metódusa is, ami azt jelenti, hogy ez is kielégíti a kezelőfelületet.

számomra ez leegyszerűsíti a dolgokat gondolni egy ServeMux, mint csak, hogy egy különleges fajta kezelő, amely ahelyett, hogy a válasz maga továbbítja a kérést egy második kezelő. Ez nem annyira ugrás, mint amilyennek először hangzik – a kezelők összekapcsolása meglehetősen általános a Go-ban.

egyéni kezelők

hozzunk létre egy egyéni kezelőt, amely az aktuális helyi idővel válaszol egy adott formátumban:

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

a pontos kód itt nem túl fontos.

csak az számít igazán, hogy van egy objektumunk (ebben az esetben ez egy timeHandler struct, de lehet string vagy függvény, vagy bármi más is), és végrehajtottunk egy módszert az aláírással ServeHTTP(http.ResponseWriter, *http.Request) rajta. Ez minden, amire szükségünk van, hogy egy kezelő legyen.

beágyazzuk ezt egy konkrét példába:

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

a main függvényben a timeHandler pontosan ugyanúgy inicializáltuk a timeHandler szimbólum a mutató megjelenítéséhez. Ezután az előző példához hasonlóan a mux.Handle függvényt használjuk ennek regisztrálására a ServeMux-nál.

Most, amikor futtatjuk az alkalmazást, a ServeMux minden /time kérést továbbít a timeHandler.ServeHTTP módszerünkre.

Menj és próbáld ki: http://localhost:3000/time.

figyeljük meg, hogy mi is könnyen újra a timeHandler több útvonalon:

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

kezelőként működik

egyszerű esetekben (például a fenti példában) új egyéni típusok és servehttp módszerek meghatározása kissé bőbeszédű. Nézzünk meg egy alternatív megközelítést, ahol kihasználjuk a Go http.HandlerFunc típusát, hogy egy normál funkciót kényszerítsünk a kezelőfelület kielégítésére.

minden olyan függvény, amelynek aláírása func(http.ResponseWriter, *http.Request) átalakítható HandlerFunc típusra. Ez azért hasznos, mert a HandleFunc objektumok beépített ServeHTTP metódussal rendelkeznek, amely – meglehetősen ügyesen és kényelmesen – végrehajtja az eredeti függvény tartalmát.

Ha ez zavarónak hangzik, próbálja meg megnézni a megfelelő forráskódot. Látni fogja, hogy ez egy nagyon tömör módja annak, hogy egy funkció kielégítse a kezelőfelületet.

reprodukáljuk a timeHandler alkalmazást ezzel a technikával:

Fájl: fő.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)}

valójában egy függvény konvertálása HandlerFunc típusra, majd hozzáadása egy ilyen ServeMux-hoz olyan gyakori, hogy a Go parancsikont biztosít: a mux.HandleFunc módszer.

így nézett volna ki a main() függvény, Ha ezt a parancsikont használtuk volna:

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

a legtöbb esetben egy funkció, mint egy kezelő, mint ez jól működik. De van egy kis korlátozás, amikor a dolgok bonyolultabbá válnak.

valószínűleg észrevette, hogy a korábbi módszerrel ellentétben az időformátumot a timeHandler függvényben kellett kódolni. Mi történik, ha információt vagy változókat akarunk átadni a main() – ből egy kezelőnek?

egy ügyes megközelítés az, hogy a kezelő logikánkat bezárjuk, és bezárjuk a használni kívánt változókat:

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

a timeHandler funkció most már egy finoman más szerepet. Ahelyett, hogy a függvényt kezelővé kényszerítenénk (mint korábban tettük), most egy kezelő visszaadására használjuk. Két kulcsfontosságú eleme van ennek a munkának.

először létrehozza a fnnévtelen függvényt, amely hozzáfér a &kötőjel; vagy bezárja a format változót, amely lezárást képez. Függetlenül attól, hogy mit csinálunk a bezárással, mindig képes lesz elérni azokat a változókat, amelyek lokálisak ahhoz a hatókörhöz, amelyben létrehozták – ami ebben az esetben azt jelenti, hogy mindig hozzáférhet a format változóhoz.

másodszor a lezárásunk aláírással rendelkezik func(http.ResponseWriter, *http.Request). Mint korábban emlékszel, ez azt jelenti, hogy átalakíthatjuk HandlerFunc típusba (úgy, hogy kielégítse a kezelő felületet). A timeHandler függvény ezt az átalakított zárást adja vissza.

ebben a példában éppen egy egyszerű karakterláncot adtunk át egy kezelőnek. De egy valós alkalmazásban ezt a módszert használhatja adatbázis-kapcsolat, sablontérkép vagy bármely más alkalmazásszintű kontextus átadására. Ez egy jó alternatíva a globális változók használatához, és további előnye, hogy tiszta, önálló kezelőket készít a teszteléshez.

ugyanazt a mintát is láthatja, mint:

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

vagy implicit konverziót használ a HandlerFunc típusra visszatéréskor:

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

A DefaultServeMux

valószínűleg láttad DefaultServeMux említett sok helyen, a legegyszerűbb Hello World példák a GO forráskód.

hosszú időbe telt, mire rájöttem, hogy ez nem valami különleges. A DefaultServeMux csak egy egyszerű ol ‘ ServeMux, mint amit már használunk, amely alapértelmezés szerint példányosul a HTTP csomag használatakor. Itt van a megfelelő sor a Go forrásból:

var DefaultServeMux = NewServeMux()

általában nem szabad használni a DefaultServeMux-ot, mert biztonsági kockázatot jelent.

mivel a DefaultServeMux globális változóban van tárolva, bármely csomag hozzáférhet hozzá és regisztrálhat egy útvonalat – beleértve az alkalmazás által importált harmadik féltől származó csomagokat is. Ha az egyik ilyen harmadik féltől származó csomag veszélybe kerül, akkor a DefaultServeMux segítségével rosszindulatú kezelőt tehet ki az internetre.

tehát ökölszabályként jó ötlet elkerülni a DefaultServeMux – ot, ehelyett a saját helyi hatókörű ServeMux-ot használja, mint eddig. De ha mégis úgy dönt, hogy használja…

a HTTP csomag néhány parancsikont tartalmaz a DefaultServeMux: http használatához.Fogantyú és http.HandleFunc. Ezek pontosan ugyanazt teszik, mint a névrokon funkcióik, amelyeket már megnéztünk, azzal a különbséggel, hogy kezelőket adnak a Defaultservemuxhoz az Ön által létrehozott helyett.

ezenkívül a ListenAndServe visszatér a DefaultServeMux használatához, ha nincs más kezelő (vagyis a második paraméter értéke nil).

tehát utolsó lépésként frissítsük a timeHandler alkalmazást a DefaultServeMux helyett:

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

Ha tetszett ez a blogbejegyzés, ne felejtsd el, hogy nézd meg az új könyv arról, hogyan kell építeni professzionális webes alkalmazások Go!

Kövess a Twitteren @ajmedwards.

Az ebben a bejegyzésben szereplő összes kódrészlet szabadon használható az MIT licenc alatt.



Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.