Gin源码解析和例子——中间件(middleware)

Gin源码解析和例子——中间件(middleware)

中间件的过程类似于django中间件,

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/breaksoftware/article/details/84765060

        在《Gin源码解析和例子——路由》一文中,我们已经初识中间件。本文将继续探讨这个技术。(转载请指明出于breaksoftware的csdn博客)

        Gin的中间件,本质是一个匿名回调函数。这和绑定到一个路径下的处理函数本质是一样的。

        再以Engine的Default方法为例

  1.  
    func Default() *Engine {
  2.  
    debugPrintWARNINGDefault()
  3.  
    engine := New()
  4.  
    engine.Use(Logger(), Recovery())
  5.  
    return engine
  6.  
    }

        第4行就让该Engine使用了Logger和Revoery两个中间件。Use方法将新增的中间件加入到中间件集合中

  1.  
    // Use adds middleware to the group, see example code in github.
  2.  
    func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
  3.  
    group.Handlers = append(group.Handlers, middleware...)
  4.  
    return group.returnObj()
  5.  
    }

        因为是append,所以后加入的中间件排在集合后面。理解这个特性对我们正确使用中间件很重要。

        再回顾下之前介绍的路由的代码

  1.  
    r := gin.Default()
  2.  
     
  3.  
    // Ping test
  4.  
    r.GET("/ping", func(c *gin.Context) {
  5.  
    c.String(http.StatusOK, "pong")
  6.  
    })

        host:port/ping下的请求,将被路由到输出pong的匿名函数里。GET方法封装了handle方法

  1.  
    func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
  2.  
    absolutePath := group.calculateAbsolutePath(relativePath)
  3.  
    handlers = group.combineHandlers(handlers)
  4.  
    group.engine.addRoute(httpMethod, absolutePath, handlers)
  5.  
    return group.returnObj()
  6.  
    }

        这儿注意下第3行,上面这个匿名函数似乎是和其他匿名函数合并成一个匿名函数集合。然后再在第4行和绝对路径绑定。

  1.  
    func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
  2.  
    finalSize := len(group.Handlers) + len(handlers)
  3.  
    if finalSize >= int(abortIndex) {
  4.  
    panic("too many handlers")
  5.  
    }
  6.  
    mergedHandlers := make(HandlersChain, finalSize)
  7.  
    copy(mergedHandlers, group.Handlers)
  8.  
    copy(mergedHandlers[len(group.Handlers):], handlers)
  9.  
    return mergedHandlers
  10.  
    }

        这儿合并的就是中间件集合(group.Handlers)。第7~8行代码,告诉我们中间件的回调要先于用户定义的路径处理函数。那么上例中,mergeHandlers中的成员是【logger回调,recovery回调,GET的匿名回调】。

        这样,每个路径的回调函数链都将包含中间件的回调,即【logger回调,recovery回调】。

        我再看一个最简单的中间件的实现

  1.  
    func MiddlewareDemo() gin.HandlerFunc {
  2.  
    return func(c *gin.Context) {
  3.  
    c.Next()
  4.  
    }
  5.  
    }

        这个中间件只是返回了一个匿名函数,该函数内部需要调用Conext的Next函数来驱动执行之后的handler。

  1.  
    func (c *Context) Next() {
  2.  
    c.index++
  3.  
    for s := int8(len(c.handlers)); c.index < s; c.index++ {
  4.  
    c.handlers[c.index](c)
  5.  
    }
  6.  
    }

        这也是Gin设计中比较奇葩的地方:

  • Context的Next方法让合并之后的handlers中的回调执行
  • handlers中的回调调用Context的Next方法以驱动下个回调执行

        如果我们不看Next的实现,单从上面的话中可以感觉到似乎逻辑进入了一种异常循环的状态。其实Gin使用了一个Context中的index变量来解决了这个问题。于是中间件、框架和路径对应的回调之前的关系是

        我们看个例子

    

package main
 
import (
    "log"
    "net/http"
 
    "github.com/gin-gonic/gin"
)
 
func MiddlewareA() gin.HandlerFunc {
    return func(c *gin.Context) {
        log.Println("MiddlewareA before request")
        // before request
        c.Next()
        // after request
        log.Println("MiddlewareA after request")
    }
}
 
func MiddlewareB() gin.HandlerFunc {
    return func(c *gin.Context) {
        log.Println("MiddlewareB before request")
        // before request
        c.Next()
        // after request
        log.Println("MiddlewareB after request")
    }
}
 
// This function's name is a must. App Engine uses it to drive the requests properly.
func main() {
    // Starts a new Gin instance with no middle-ware
    r := gin.New()
    r.Use(MiddlewareA(), MiddlewareB())
    r.GET("/ping", func(c *gin.Context) {
        c.String(http.StatusOK, "pong")
        log.Println("pong")
    })
    r.Run(":8080")
}

--------------------- 
作者:breaksoftware 
来源:CSDN 
原文:https://blog.csdn.net/breaksoftware/article/details/84765060 
版权声明:本文为博主原创文章,转载请附上博文链接!

 

  1.         触发一次请求后,服务器的日志输出是
  1.  
    2018/12/03 16:07:30 MiddlewareA before request
  2.  
    2018/12/03 16:07:30 MiddlewareB before request
  3.  
    2018/12/03 16:07:30 pong
  4.  
    2018/12/03 16:07:30 MiddlewareB after request
  5.  
    2018/12/03 16:07:30 MiddlewareA after request

        可以看到,结果符合我们对代码的解读。

posted on 2019-06-13 11:49  王大拿  阅读(910)  评论(0)    收藏  举报

导航