Go语言的Http 中间件实现 - Go语言中文社区

Go语言的Http 中间件实现


当你正在构建一个Web应用程序有可能要运行许多(甚至全部)的HTTP请求一些共享功能,你可能想记录每一个request,gzip压缩的每个response,或者做一些繁重的处理或者缓存检查。

实现这个共享功能的一种方法是将其设置为中间件,他可以作为一个独立的程序,在正常的handlers处理之前。根本不需要重写代码:如果你想用一个中间件,就把它加上应用中;如果你改变主意了,去掉就好了。就这么简单。


  1. ServeMux => Middleware Handler => Application Handler 

这篇文章,我会给大家介绍怎么自己去实现一个自定义的middleware模式。以及通过使用第三方的中间件软件包的一些具体的实例。

基本原则:

在Go语言中实现和使用middleware是非常简单的。

使我们的中间件能搞满足 http.handlers 这个接口

建立一个 handlers 链,使其能够满足中间件的 handler 和 正常应用的 handler,并且能够注册到 http.ServeMux

我来解释如何实现:

首先你要知道go 的http handle,这里假设你是知道的


  1. func messageHandler(message string) http.Handler { 
  2.  
  3. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 
  4.  
  5. w.Write([]byte(message) 
  6.  
  7. }) 
  8.  

这上面这个代码片段里面我们的逻辑很简单只是一个简单的 w.Write() 然后我们使用 http.HandlerFunc 适配器来转化这个闭包,并返回。

我们可以使用一个相同的方法来创建一个 handler 链。可以使用 handler 代替参数 string 传进闭包,然后把控制 handler 给传进来的 handler,并且调用 ServeHTTP() 方法。

这给了我们一个完整的模式构建中间件:


  1. func exampleMiddleware(next http.Handler) http.Handler { 
  2.  
  3. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 
  4.  
  5. // Our middleware logic goes here... 
  6.  
  7. next.ServeHTTP(w, r) 
  8.  
  9. }) 
  10.  

你注意到这个中间件有一个这样的函数结构 func(http.Handler) http.Handler 。它接受一个 handler 作为参数,并且返回一个 handler。这里有两个很有用的原因:

因为这个函数返回一个句柄可以直接供中间件注册

我们可以建立任意长度的 handler 链来通过中间件的方法互相嵌套

比如:


  1. http.Handle("/", middlewareOne(middlewareTwo(finalHandler))) 

控制流说明:

让我们来看一个带有多个中间件的例子,并且把日志输出到控制台:


  1. package main 
  2.  
  3. import ( 
  4.  
  5. "log" 
  6.  
  7. "net/http" 
  8.  
  9.  
  10. func middlewareOne(next http.Handler) http.Handler { 
  11.  
  12. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 
  13.  
  14. log.Println("Executing middlewareOne"
  15.  
  16. next.ServeHTTP(w, r) 
  17.  
  18. log.Println("Executing middlewareOne again"
  19.  
  20. }) 
  21.  
  22.  
  23. func middlewareTwo(next http.Handler) http.Handler { 
  24.  
  25. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 
  26.  
  27. log.Println("Executing middlewareTwo"
  28.  
  29. if r.URL.Path != "/" { 
  30.  
  31. return 
  32.  
  33.  
  34. next.ServeHTTP(w, r) 
  35.  
  36. log.Println("Executing middlewareTwo again"
  37.  
  38. }) 
  39.  
  40.  
  41. func final(w http.ResponseWriter, r *http.Request) { 
  42.  
  43. log.Println("Executing finalHandler"
  44.  
  45. w.Write([]byte("OK")) 
  46.  
  47.  
  48. func main() { 
  49.  
  50. finalHandler := http.HandlerFunc(final
  51.  
  52. http.Handle("/", middlewareOne(middlewareTwo(finalHandler))) 
  53.  
  54. http.ListenAndServe(":3000", nil) 
  55.  

然后我们执行 go run main.go 在浏览器打开http://localhost:3000。 你会看到下面的输出。

4626AADF-4508-4094-B16B-59356D45FB55

我们能够很清楚的看到handle的流程控制。我们嵌套他们的返回顺序。我们可以通过中间件中得 return 随时停止handle链的控制。

在上面的代码中我们在middlewareTwo function包含了retrun 语句。我们在浏览器中打开http://localhost:3000/foo,我们会看到。


  1. 2015/12/19 04:21:57 Executing middlewareOne 
  2.  
  3. 2015/12/19 04:21:57 Executing middlewareTwo 
  4.  
  5. 2015/12/19 04:21:57 Executing middlewareOne again 
  6.  
  7. 2015/12/19 04:21:57 Executing middlewareOne 
  8.  
  9. 2015/12/19 04:21:57 Executing middlewareTwo 
  10.  
  11. 2015/12/19 04:21:57 Executing middlewareOne again 

我们实现一个真实的项目的示例:

我们实现一个判断请求是不是XMl的功能,我们要实现一个中间件。用来检查的请求体的存在。检查请求体,以确保它是XML。如果其中检查失败,我希望我们的中间件输出错误信息然后终止我们的handle处理。


  1. package main 
  2.  
  3. import ( 
  4.  
  5. "bytes" 
  6.  
  7. "net/http" 
  8.  
  9.  
  10. func enforceXMLHandler(next http.Handler) http.Handler { 
  11.  
  12. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 
  13.  
  14. // Check for a request body 
  15.  
  16. if r.ContentLength == 0 { 
  17.  
  18. http.Error(w, http.StatusText(400), 400) 
  19.  
  20. return 
  21.  
  22.  
  23. // Check its MIME type 
  24.  
  25. buf := new(bytes.Buffer) 
  26.  
  27. buf.ReadFrom(r.Body) 
  28.  
  29. if http.DetectContentType(buf.Bytes()) != "text/xml; charset=utf-8" { 
  30.  
  31. http.Error(w, http.StatusText(415), 415) 
  32.  
  33. return 
  34.  
  35.  
  36. next.ServeHTTP(w, r) 
  37.  
  38. }) 
  39.  
  40.  
  41. func main() { 
  42.  
  43. finalHandler := http.HandlerFunc(final
  44.  
  45. http.Handle("/", enforceXMLHandler(finalHandler)) 
  46.  
  47. http.ListenAndServe(":3000", nil) 
  48.  
  49.  
  50. func final(w http.ResponseWriter, r *http.Request) { 
  51.  
  52. w.Write([]byte("OK")) 
  53.  

为了检验我们的中间件是否实现了这个功能,我们首先创建一个XML文件。


  1. 版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
    原文链接:https://blog.csdn.net/weixin_34184561/article/details/90396719
    站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-06-28 00:28:23
  • 阅读 ( 1239 )
  • 分类:Go

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢