以前咱们项目中遇到的问题是代码重复。在处理请求以前,咱们一般须要进行日志记录,异常捕获,用户认证等操做。而且这些操做须要被应用到每个处理handler中。golang
使用golang的基础包net/http
建立了一个很是简单的应用web
import "net/http" type DefaultHandler struct {} func (DefaultHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { path := r.URL.Path _, _ = w.Write([]byte(path + " wellcome to http server.")) } func userLogin(w http.ResponseWriter, r *http.Request) { path := r.URL.Path _, _ = w.Write([]byte(path + " wellcome to http server by handleFunc.")) } func main() { http.Handle("/", DefaultHandler{}) http.HandleFunc("/apis", userLogin) _ = http.ListenAndServe("0.0.0.0:8080", nil) }
http.Handle
接受两个参数,第二个参数类型是http.Handler, 它是一个接口类型包含了ServeHTTP(ResponseWriter, *Request)
方法,因此任何实现了该方法的类型,均可以看成http.Handler
来使用,传入http.Handle
方法中。api
如今,咱们想要记录每一个请求的耗时:框架
func userLogin(w http.ResponseWriter, r *http.Request) { t := time.Now() path := r.URL.Path _, _ = w.Write([]byte(r.Method + path + " wellcome to http server by handleFunc.")) t2 := time.Now() log.Printf("[%s] %q %v\n", r.Method, r.URL.String(), t2.Sub(t)) }
对于一个handler 目前的处理办法仍是可以知足, 可是当有更多的handler方法时,就显得很麻烦了。函数
咱们想要相似其余系统中的中间件系统。对于golang标准库已经有这种handler http.StripPrefix(prefix, handler) and http.TimeoutHandler(handler, duration, message)
。 他们都把一个handler 当作参数,而且返回值也是handler。这样咱们就能够将一个handler嵌套在另一个handler中,并按照下面的方式来使用它了。loggingHandler(recoverHandler(indexHandler))
日志
Alice 就是完成链式handler的包,只是她提供的使用方法变得更加优雅。code
func main() { commonHandlers := alice.New(loggingHandler, recoverHandler) http.Handle("/about", commonHandlers.ThenFunc(aboutHandler)) http.Handle("/", alice.New(commonHandlers, bodyParserHandler).ThenFunc(indexHandler)) http.ListenAndServe(":8080", nil) }
有了alice,咱们仍是没法使用http.StripPrefix(prefix, handler)
这样的handler由于它不是 func (http.Handler) http.Handler
类型的方法,每次须要使用这种handler时,须要对其进行封装server
func myStripPrefix(h http.Handler) http.Handler { return http.StripPrefix("/old", h) }
咱们把两个处理函数改成返回 方法+path中间件
func userLogin(w http.ResponseWriter, r *http.Request) { w.Write([]byte(r.URL.Path)) } func userLogin2(w http.ResponseWriter, r *http.Request) { w.Write([]byte(r.URL.Path)) }
在添加一个处理日志记录的方法接口
func logHandler(next http.HandlerFunc) http.HandlerFunc { fn := func(w http.ResponseWriter, r *http.Request) { log.Printf("%s %s", r.Method, r.URL.Path) next.ServeHTTP(w, r) } return http.HandlerFunc(fn) }
使用
http.Handle("/login/1", logHandler(userLogin)) http.HandleFunc("/login/2", logHandler(userLogin2))