writing http middleware in go
TRANSCRIPT
Writing HTTP Middleware In Go
Shiju Varghese
GopherCon India 2016
Agenda
• Introduction to HTTP Middleware
• Writing HTTP Middleware with Negroni
HTTP Handlers
! !!!! !
Handlers are responsible for writing headers and bodies !into HTTP responses. !!//ServeHTTPshouldwritereplyheadersanddata//totheResponseWriterandthenreturn.!typeHandlerinterface{ServeHTTP(ResponseWriter,*Request)}
HTTP Middleware• Pluggable and self-contained piece of code that
wraps web application handlers.!
• Components that work as another layer in the request handling cycle, which can execute some logic before or after executing your HTTP application handlers.!
• Great for implementing cross-cutting concerns: Authentication, authorization, caching, logging, etc.
!
Using StripPrefix to Wraps http.FileServer handler
packagemain!import("net/http")!funcmain(){//ToserveadirectoryondiskunderanalternateURL//path(/public/),useStripPrefixtomodifytherequest//URL'spathbeforetheFileServerseesit:!fs:=http.FileServer(http.Dir("public"))http.Handle("/public/",http.StripPrefix("/public/",fs)) }
funcStripPrefix(prefixstring,hHandler)Handler{ifprefix==""{returnh}returnHandlerFunc(func(wResponseWriter,r*Request){ifp:=strings.TrimPrefix(r.URL.Path,prefix);len(p)<len(r.URL.Path){r.URL.Path=ph.ServeHTTP(w,r)}else{NotFound(w,r)}})}
StripPrefix Function
Pattern for Writing HTTP Middleware
.
funcmiddlewareHandler(nexthttp.Handler)http.Handler{returnhttp.HandlerFunc(func(whttp.ResponseWriter,r*http.Request){//Ourmiddlewarelogicgoesherebefore //executingapplicationhandlernext.ServeHTTP(w,r)//Ourmiddlewarelogicgoeshereafter //executingapplicationhandler})}
Writing a Logging MiddlewarefuncloggingHandler(nexthttp.Handler)http.Handler{returnhttp.HandlerFunc(func(whttp.ResponseWriter,r*http.Request){! //Beforeexecutingthehandler!start:=time.Now()log.Printf("Started%s%s",r.Method,r.URL.Path)next.ServeHTTP(w,r)! //Afterexecutingthehandler!log.Printf("Completed%sin%v",r.URL.Path,time.Since(start))})}
• Parameter of type http.Handler!• Returns http.Handler type
!!funcindex(whttp.ResponseWriter,r*http.Request){fmt.Fprintf(w,"Welcome!")}funcabout(whttp.ResponseWriter,r*http.Request){fmt.Fprintf(w,"About")}funcmain(){http.Handle("/",loggingHandler(http.HandlerFunc(index)))http.Handle("/about",loggingHandler(http.HandlerFunc(about)))!server:=&http.Server{Addr:":3000",}server.ListenAndServe()}
Using the Logging Middleware
Middleware Chaining
http.Handle("/",middlewareFirst(middlewareSecond(loggingHandler(http.HandlerFunc(index)))))
Using HTTP Middleware with Negroni Package
Install Negroni:!
$ go get github.com/codegangsta/negroni!
Import Negroni package:!
import "github.com/codegangsta/negroni"
//Handlerhandlerisaninterfacethatobjects//canimplementtoberegisteredtoserveasmiddleware//intheNegronimiddlewarestack.//ServeHTTPshouldyieldtothenextmiddleware//inthechainbyinvokingthenexthttp.HandlerFunc//passedin.////IftheHandlerwritestotheResponseWriter,thenexthttp.HandlerFuncshouldnotbeinvoked.typeHandlerinterface{ServeHTTP(rwhttp.ResponseWriter,r*http.Request,nexthttp.HandlerFunc)}!//HandlerFuncisanadaptertoallowtheuseof//ordinaryfunctionsasNegronihandlers.typeHandlerFuncfunc(rwhttp.ResponseWriter,r*http.Request,nexthttp.HandlerFunc)!func(hHandlerFunc)ServeHTTP(rwhttp.ResponseWriter,r*http.Request,nexthttp.HandlerFunc){h(rw,r,next)}
Writing HTTP Middleware with Negroni
funcMyMiddleware(rwhttp.ResponseWriter,r*http.Request,nexthttp.HandlerFunc){//logicbeforeexecutingthenexthandlernext(rw,r)//logicafterexecutingthenexthandler}
Mapping Middleware Chaining with Negroni
funcmain(){mux:=http.NewServeMux()//mapyourroutesheren:=negroni.New()//YoucanmapittothehandlerchainwiththeUsefunction:n.Use(negroni.HandlerFunc(MyMiddleware))n.UseHandler(mux)server:=&http.Server{Addr:":8080",Handler:n,}server.ListenAndServe()}
Register Middleware Handlers for Specific Routes
router:=mux.NewRouter()adminRoutes:=mux.NewRouter()!//addadminrouteshere...!//AddroutespecificMiddlewareto“/admin”routerouter.Handle("/admin",negroni.New(Middleware1,Middleware2,negroni.Wrap(adminRoutes),))
Applying an Authentication Middleware into Specific Routes
//MiddlewareforvalidatingJWTtokensfuncAuthorize(whttp.ResponseWriter,r*http.Request,nexthttp.HandlerFunc){//validatethetokentoken,err:=jwt.ParseFromRequest(r,func(token*jwt.Token)(interface{},error){!//Verifythetokenwithpublickey,whichisthecounterpartofprivatekeyreturnverifyKey,nil})!iferr!=nil{switcherr.(type){!case*jwt.ValidationError://JWTvalidationerrorvErr:=err.(*jwt.ValidationError)!switchvErr.Errors{casejwt.ValidationErrorExpired://JWTexpiredDisplayAppError(w,err,"AccessTokenisexpired,getanewToken",401)return!default:DisplayAppError(w,err,"ErrorwhileparsingtheAccessToken!",500)return}!default:DisplayAppError(w,err,"ErrorwhileparsingAccessToken!",500)return}!}iftoken.Valid{next(w,r)}else{DisplayAppError(w,err,"InvalidAccessToken",401)!}}
//Routesfor“/users”pathfuncSetUserRoutes(router*mux.Router)*mux.Router{router.HandleFunc("/users/register",controllers.Register).Methods("POST")router.HandleFunc("/users/login",controllers.Login).Methods("POST")returnrouter}!//Routesfor“/tasks”pathfuncSetTaskRoutes(router*mux.Router)*mux.Router{taskRouter:=mux.NewRouter()taskRouter.HandleFunc("/tasks",controllers.CreateTask).Methods("POST")taskRouter.HandleFunc("/tasks/{id}",controllers.UpdateTask).Methods(“PUT”)...!//ApplyAuthorizemiddlewareinto“/tasks”path!router.PathPrefix("/tasks").Handler(negroni.New(negroni.HandlerFunc(Authorize),negroni.Wrap(taskRouter),))returnrouter}