golang 编程笔记

【golang】 http的中间件

2020-03-12  本文已影响0人  dongzd

中间件Middleware

所谓中间件,就是连接上下级不同功能的函数或者软件,通常进行一些包裹函数的行为,为被包裹函数提供添加一些功能或行为。前文的HandleFunc就能把签名为 func(w http.ResponseWriter, r *http.Reqeust)的函数包裹成handler。这个函数也算是中间件。

这里我们以HTTP请求的中间件为例子,提供一个log中间件,能够打印出每一个请求的log。

go的http中间件很简单,只要实现一个函数签名为func(http.Handler) http.Handler的函数即可。http.Handler是一个接口,接口方法我们熟悉的为serveHTTP。返回也是一个handler。因为go中的函数也可以当成变量传递或者或者返回,因此也可以在中间件函数中传递定义好的函数,只要这个函数是一个handler即可,即实现或者被handlerFunc包裹成为handler处理器。

func middlewareHandler(next http.Handler) http.Handler{
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
        // 执行handler之前的逻辑
        next.ServeHTTP(w, r)
        // 执行完毕handler后的逻辑
    })
}

这种方式在Elixir的Plug框架中很流行,思想偏向于函数式范式。熟悉python的朋友一定也想到了装饰器。闲话少说,来看看go是如何实现的吧:

func loggingHandler(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        log.Printf("Started %s %s", r.Method, r.URL.Path)
        next.ServeHTTP(w, r)
        log.Printf("Comleted %s in %v", r.URL.Path, time.Since(start))
    })
}

func main() {
    http.Handle("/", loggingHandler(http.HandlerFunc(index)))
    http.ListenAndServe(":8000", nil)
}

loggingHandler即是一个中间件函数,将请求的和完成的时间处理。可以看见请求或go的输出:

2016/12/04 21:18:13 Started GET /
2016/12/04 21:18:13 Comleted / in 13.365µs
2016/12/04 21:18:20 Started GET /
2016/12/04 21:18:20 Comleted / in 17.541µs

既然中间件是一种函数,并且签名都是一样,那么很容易就联想到函数一层包一层的中间件。再添加一个函数,然后修改main函数:

func hook(next http.Handler) http.Handler{
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Println("before hook")
        next.ServeHTTP(w, r)
        log.Println("after hook")

    })
}

func main() {
    http.Handle("/", hook(loggingHandler(http.HandlerFunc(index))))
    http.ListenAndServe(":8000", nil)
}

在loggingHandler再包了一层hook,可以看到输出为:

2016/12/04 21:26:30 before hook
2016/12/04 21:26:30 Started GET /
2016/12/04 21:26:30 Comleted / in 14.016µs
2016/12/04 21:26:30 after hook

函数调用形成了一条链,可以是在这条链上做很多事情。当然go的写法上,比起elixir的|>的符号,优雅性略差。

上一篇下一篇

猜你喜欢

热点阅读