在编写web应用中,我们常常会遇到这样的需求,比如,我们需要上报每个API的运行时间到运维监控系统。这时候你可以像下述代码一样将统计的逻辑写到每个路由函数中。

func someApi(w http.ResponseWriter, r *http.Request) {
	start := time.Now()
	// your logic
	metrics.Upload(time.Since(start))
}
DRYnet/http

准备工作

func hello(w http.ResponseWriter, r *http.Request) {
	log.Println("execute hello func")
	w.Write([]byte("hello, world"))
}

func main() {
	http.Handle("/", http.HandlerFunc(hello))
	http.ListenAndServe(":3000", nil)
}
Handler

中间件的实现

func middlewareOne(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.Println("middleware one start") // before logic
		next.ServeHTTP(w, r)
		log.Println("middleware one end") // after logic
	})
}
middlewareOnenexthttp.Handlerhttp.Handlernext.ServeHTTP(w, r)nextnext(w, r)HandlerHandlerFuncServeHTTP

下面是三者的定义:

// A Handler responds to an HTTP request.
type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}

type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
	f(w, r)
}

HandlerHandler
HandlerFuncfunc(ResponseWriter, *Request)ServerHTTPHandleFuncHandlerServerHTTPHandlerFunc

三者关系如下图所示:

Handler
func main() {
	http.Handle("/", middlewareOne(http.HandlerFunc(hello)))
	http.ListenAndServe(":3000", nil)
}
http://127.0.0.1:3000/
2019/12/middleware one start
2019/12/execute hello func
2019/12/middleware one end

当然,如果你想应用多个中间件,你只需要再套上一层,例如下述代码:

func middlewareTwo(next http.Handler) http.Handler {
	return http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
		log.Println("middleware two start")
		next.ServeHTTP(w, r)
		log.Println("middleware two end")
	})
}

func main() {
	http.Handle("/", middlewareTwo(middlewareOne(http.HandlerFunc(hello))))
	http.ListenAndServe(":3000", nil)
}

我画出了该函数链的执行流程,如下图所示:

hellomiddlewareOnehellomiddlewareTwomiddlewareTwo

总结

Flaskbefore_requestafter_requestnext.ServeHTTPbefore_requestafter_request

参考