7.9 GO中间件(Middleware )

中间件是一种计算机软件,可为操作系统提供的软件应用程序提供服务,以便于各个软件之间的沟通,特别是系统软件和应用软件。广泛用于web应用和面向服务的体系结构等。

纵观GO语言,中间件应用比较普遍,主要应用:

  • 记录对服务器发送的请求(request)
  • 处理服务器响应(response )
  • 请求和处理之间做一个权限认证工作
  • 远程调用
  • 安全
  • 等等

中间件处理程序是简单的http.Handler,它包装另一个http.Handler做请求的一些预处理和/或后处理。它被称为“中间件”,因为它位于Go Web服务器和实际处理程序之间的中间位置。

中间件(Middleware ) - 图1

下面是一些中间件例子

记录日志中间件

  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "net/http"
  6. )
  7. func logging(f http.HandlerFunc) http.HandlerFunc {
  8. return func(w http.ResponseWriter, r *http.Request) {
  9. log.Println(r.URL.Path)
  10. f(w, r)
  11. }
  12. }
  13. func foo(w http.ResponseWriter, r *http.Request) {
  14. fmt.Fprintln(w, "foo")
  15. }
  16. func bar(w http.ResponseWriter, r *http.Request) {
  17. fmt.Fprintln(w, "bar")
  18. }
  19. func main() {
  20. http.HandleFunc("/foo", logging(foo))
  21. http.HandleFunc("/bar", logging(bar))
  22. http.ListenAndServe(":8080", nil)
  23. }

访问 http://localhost:8080/foo

返回结果

foo

将上面示例修改下,也可以实现相同的功能。

  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "net/http"
  6. )
  7. func foo(w http.ResponseWriter, r *http.Request) {
  8. fmt.Fprintln(w, "foo")
  9. }
  10. func bar(w http.ResponseWriter, r *http.Request) {
  11. fmt.Fprintln(w, "bar")
  12. }
  13. func loggingMiddleware(next http.Handler) http.Handler {
  14. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  15. log.Println(r.URL.Path)
  16. next.ServeHTTP(w, r)
  17. })
  18. }
  19. func main() {
  20. http.Handle("/foo", loggingMiddleware(http.HandlerFunc(foo)))
  21. http.Handle("/bar", loggingMiddleware(http.HandlerFunc(bar)))
  22. http.ListenAndServe(":8080", nil)
  23. }

访问 http://localhost:8080/foo

返回结果

foo

多中间件例子

  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "net/http"
  6. "time"
  7. )
  8. type Middleware func(http.HandlerFunc) http.HandlerFunc
  9. // Logging logs all requests with its path and the time it took to process
  10. func Logging() Middleware {
  11. // Create a new Middleware
  12. return func(f http.HandlerFunc) http.HandlerFunc {
  13. // Define the http.HandlerFunc
  14. return func(w http.ResponseWriter, r *http.Request) {
  15. // Do middleware things
  16. start := time.Now()
  17. defer func() { log.Println(r.URL.Path, time.Since(start)) }()
  18. // Call the next middleware/handler in chain
  19. f(w, r)
  20. }
  21. }
  22. }
  23. // Method ensures that url can only be requested with a specific method, else returns a 400 Bad Request
  24. func Method(m string) Middleware {
  25. // Create a new Middleware
  26. return func(f http.HandlerFunc) http.HandlerFunc {
  27. // Define the http.HandlerFunc
  28. return func(w http.ResponseWriter, r *http.Request) {
  29. // Do middleware things
  30. if r.Method != m {
  31. http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
  32. return
  33. }
  34. // Call the next middleware/handler in chain
  35. f(w, r)
  36. }
  37. }
  38. }
  39. // Chain applies middlewares to a http.HandlerFunc
  40. func Chain(f http.HandlerFunc, middlewares ...Middleware) http.HandlerFunc {
  41. for _, m := range middlewares {
  42. f = m(f)
  43. }
  44. return f
  45. }
  46. func Hello(w http.ResponseWriter, r *http.Request) {
  47. fmt.Fprintln(w, "hello world")
  48. }
  49. func main() {
  50. http.HandleFunc("/", Chain(Hello, Method("GET"), Logging()))
  51. http.ListenAndServe(":8080", nil)
  52. }

中间件本身只是将其http.HandlerFunc作为其参数之一,包装它并返回一个新http.HandlerFunc的服务器来调用。在这里,我们定义了一种新类型Middleware,最终可以更容易地将多个中间件链接在一起。

当然我们也可以改成如下形式

  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "net/http"
  6. "time"
  7. )
  8. type Middleware func(http.Handler) http.Handler
  9. func Hello(w http.ResponseWriter, r *http.Request) {
  10. fmt.Fprintln(w, "hello world")
  11. }
  12. func Chain(f http.Handler, mmap ...Middleware) http.Handler {
  13. for _, m := range mmap {
  14. f = m(f)
  15. }
  16. return f
  17. }
  18. func Method(m string) Middleware {
  19. return func(f http.Handler) http.Handler {
  20. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  21. log.Println(r.URL.Path)
  22. if r.Method != m {
  23. http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
  24. return
  25. }
  26. f.ServeHTTP(w, r)
  27. })
  28. }
  29. }
  30. func Logging() Middleware {
  31. return func(f http.Handler) http.Handler {
  32. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  33. //log.Println(r.URL.Path)
  34. // Do middleware things
  35. start := time.Now()
  36. defer func() { log.Println(r.URL.Path, time.Since(start)) }()
  37. f.ServeHTTP(w, r)
  38. })
  39. }
  40. }
  41. func main() {
  42. http.Handle("/", Chain(http.HandlerFunc(Hello), Method("GET"), Logging()))
  43. http.ListenAndServe(":8080", nil)
  44. }

在gin框架下实现中间件

  1. r := gin.Default() 创建带有默认中间件的路由,默认是包含loggerrecovery中间件的
  2. r :=gin.new() 创建带有没有中间件的路由

示例

  1. package main
  2. import (
  3. "github.com/gin-gonic/gin"
  4. "log"
  5. "time"
  6. )
  7. func Logger() gin.HandlerFunc {
  8. return func(c *gin.Context) {
  9. t := time.Now()
  10. // Set example variable
  11. c.Set("example", "12345")
  12. // before request
  13. c.Next()
  14. // after request
  15. latency := time.Since(t)
  16. log.Print(latency) //时间 0s
  17. // access the status we are sending
  18. status := c.Writer.Status()
  19. log.Println(status) //状态 200
  20. }
  21. }
  22. func main() {
  23. r := gin.New()
  24. r.Use(Logger())
  25. r.GET("/test", func(c *gin.Context) {
  26. example := c.MustGet("example").(string)
  27. // it would print: "12345"
  28. log.Println(example)
  29. })
  30. // Listen and serve on 0.0.0.0:8080
  31. r.Run(":8080")
  32. }

以上示例也可改为

  1. package main
  2. import (
  3. "github.com/gin-gonic/gin"
  4. "log"
  5. "time"
  6. )
  7. func Logger(c *gin.Context) {
  8. t := time.Now()
  9. // Set example variable
  10. c.Set("example", "12345")
  11. // before request
  12. c.Next()
  13. // after request
  14. latency := time.Since(t)
  15. log.Print(latency) //时间 0s
  16. // access the status we are sending
  17. status := c.Writer.Status()
  18. log.Println(status) //状态 200
  19. }
  20. func main() {
  21. r := gin.New()
  22. r.Use(Logger)
  23. r.GET("/test", func(c *gin.Context) {
  24. example := c.MustGet("example").(string)
  25. // it would print: "12345"
  26. log.Println(example)
  27. })
  28. // Listen and serve on 0.0.0.0:8080
  29. r.Run(":8080")
  30. }

以上示例也可改为

  1. package main
  2. import (
  3. "github.com/gin-gonic/gin"
  4. "log"
  5. "time"
  6. )
  7. func Logger() gin.HandlerFunc {
  8. return func(c *gin.Context) {
  9. t := time.Now()
  10. // Set example variable
  11. c.Set("example", "12345")
  12. // before request
  13. c.Next()
  14. // after request
  15. latency := time.Since(t)
  16. log.Print(latency) //时间 0s
  17. // access the status we are sending
  18. status := c.Writer.Status()
  19. log.Println(status) //状态 200
  20. }
  21. }
  22. func main() {
  23. r := gin.New()
  24. r.GET("/test", Logger(), func(c *gin.Context) {
  25. example := c.MustGet("example").(string)
  26. // it would print: "12345"
  27. log.Println(example)
  28. })
  29. // Listen and serve on 0.0.0.0:8080
  30. r.Run(":8080")
  31. }

即不用r.use添加中间件,直接将Logger() 写到r.GET 方法的参数里(”/test”之后)。

更多gin中间件示例可参考 https://github.com/gin-gonic/gin

参考资料

https://drstearns.github.io/tutorials/gomiddleware/

https://gowebexamples.com/advanced-middleware/

links