内容简介:当构建 web 应用程序,可能对所有的请求会共享一些功能。例如我们每一个请求都会写入日志。在 go 语言 net/http 标准库中提供了中间件类似的函数 StripPrefix 和 TimeoutHandler。如何自己编写一个中间件呢?答案是
golang_real.jpg
当构建 web 应用程序,可能对所有的请求会共享一些功能。例如我们每一个请求都会写入日志。
- 打印 http 请求或返回的日志
- 压缩 http 请求的返回值
- 将 http 请求头保持一致
-
实现安全以及权限验证
Middleware-web.jpg
在 go 语言 net/http 标准库中提供了中间件类似的函数 StripPrefix 和 TimeoutHandler。
如何自己编写一个中间件呢?答案是
- 这个函数接收一个 http.Handler 作为参数,目的是可使用其他中间件或应用处理器,调用其他中间件或应用的方法就是调用其 HttpServe 的方法。
- 并且将 http.Handler 作为返回值,这样其他中间件可以使用 http.Handler 作为输入的参数,达到链式调用。
func middlewareHandler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // 在执行应用处理器前编译中间件的逻辑 next.ServeHTTP(w, r) // 在执行应用处理器后将会执行的编译中间件的逻辑 }) }
从上面示例我们可以看出 go 语言是支持高阶函数,通过向下传递 http.Handler 来实现中间件。
package main import ( "fmt" "log" "net/http" "time" ) func logginHandler(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("Completed %s in %v", r.URL.Path, time.Since(start)) }) } func index(w http.ResponseWriter,r *http.Request ){ log.Println("Executing index handler") fmt.Fprintf(w,"welcome!") } func about(w http.ResponseWriter, r *http.Request){ log.Println("Executing about handler") fmt.Fprintf(w,"Go Middleware") } func iconHandler(w http.ResponseWriter, r *http.Request){ } func main() { http.HandleFunc("/favicon.ico", iconHandler) indexHandler := http.HandlerFunc(index) aboutHandler := http.HandlerFunc(about) http.Handle("/",logginHandler(indexHandler)) http.Handle("/about",logginHandler(aboutHandler)) server := &http.Server{ Addr:":8080", } log.Println("Listening...") server.ListenAndServe() }
- 上面的代码关键是 logginHandler 这个日志处理器,作为中间件会拦截请求,进行处理也就是输出日志,通过
next.ServeHTTP(w,r)
然后将请求向下进行传递 - 这里创建两个简单路由处理器
index
和about
。都是向客户端输出不同的文本。 -
http.Handle("/",logginHandler(indexHandler))
通过 logginHandler 函数进行包裹,从容我们日志中间件可以拦截到发起的请求进行一些业务逻辑,业务逻辑可以位于next.ServeHTTP(w,r)
之前或之后。通过下面的输出我们可以清晰可见代码执行的顺序。
Listening... Started GET / Executing index handler Completed / in 41.129µs Started GET / Executing index handler Completed / in 50.475µs Started GET /about Executing about handler Completed /about in 49.483µs
许多书都提供代码示例,个人建议是不要 copy 过来运行看效果,自己手 coding 就可以发现一些问题,同时可以思考为什么要这样编写代码。
通过上面示例我们了解如何写一个简单中间件,这个个人看来和 nodejs 实现中间件没有什么差别。实际开发中我们往往会有多个中间件来执行业务,那么这些中间件执行顺序也是我们值得考虑的问题。
package main import( "fmt" "log" "net/http" ) func middlewareFirst(next http.Handler) http.Handler{ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){ log.Println("MiddlewareFirst - before Handler") next.ServeHTTP(w,r) log.Println("MiddlewareFirst - after Handler") }) } func middlewareSecond(next http.Handler) http.Handler{ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){ log.Println("MiddlewareSecond - before Handler") if r.URL.Path == "/message"{ if r.URL.Query().Get("password") == "123"{ log.Println("Authorized to system...") next.ServeHTTP(w,r) }else{ log.Println("Failed to authorize to the system") return } }else{ next.ServeHTTP(w,r) } log.Println("MiddlewareScond - after Handler") }) } func index(w http.ResponseWriter, r *http.Request){ log.Println("Executed index Handler") fmt.Fprintf(w,"welcome") } func message(w http.ResponseWriter, r *http.Request){ log.Println("Executed message Handler") fmt.Fprintf(w,"message...") } func iconHandler(w http.ResponseWriter, r *http.Request){ } func main() { http.HandleFunc("/favicon",iconHandler) indexHandler := http.HandlerFunc(index) messageHandler := http.HandlerFunc(message) http.Handle("/",middlewareFirst(middlewareSecond(indexHandler))) http.Handle("/message",middlewareFirst(middlewareSecond(messageHandler))) server := &http.Server{ Addr:":8080", } log.Println("Listen...") server.ListenAndServe() }
在上面代码中其实也没有什么特别之处,就是我们创建了两个 middlewareFirst
和 middlewareSecond
两个中间件并且有一定先后顺序,然后在 middlewareSecond
中写了一个对访问权限校验的逻辑。大家可以尝试地去运行一下。
th-7.jpeg
以上所述就是小编给大家介绍的《golang 网络编程(5)中间件》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。