zoukankan      html  css  js  c++  java
  • go web framework gin middleware 设计原理

    场景:一个middleware可以具体为一个函数,而由前面的gin 路由分析可得,每一个路径都对有一个HandlersChain 与其对应。

    那么实际上增加一个middleware的过程,就是将每一个路由策略加进来之前,与其绑定,这样就能使得这一类的路由到来的时候触发这个中间件生效。

    下面看看gin web framework中是如何实现的?

    首先是:gin.default()函数

    // Default returns an Engine instance with the Logger and Recovery middleware already attached.
    func Default() *Engine {
    	debugPrintWARNINGDefault()
    	engine := New()
    	engine.Use(Logger(), Recovery()) #加载Logger(), 和Recovery()中间件
    	return engine
    }

    然后查看engine.Use()

    // Use attachs a global middleware to the router. ie. the middleware attached though Use() will be
    // included in the handlers chain for every single request. Even 404, 405, static files...
    // For example, this is the right place for a logger or error management middleware.
    func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
    	engine.RouterGroup.Use(middleware...) #这个函数的参数是一个不定参数,也就是说我们可以追加的中间件没有限制
    	engine.rebuild404Handlers()
    	engine.rebuild405Handlers()
    	return engine
    }
    

    接着是engine.RouterGroup.Use()

    // Use adds middleware to the group, see example code in GitHub.
    func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
    	group.Handlers = append(group.Handlers, middleware...) #将middleware暂存在group.Handlers中
    	return group.returnObj()
    }

    这个时候,我们再来看一个路由信息的调用过程,如,我们定义一个Get的路由

    // Ping test
    r.GET("/ping", func(c *gin.Context) {
    	c.String(http.StatusOK, "pong")
    })
    

    查看r.GET函数的实现

    // GET is a shortcut for router.Handle("GET", path, handle).
    func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
    	return group.handle("GET", relativePath, handlers) #将handlers 函数以及相对路径传递给group.handle()函数
    }
    

     查看group.handle()函数的实现

    func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
    	absolutePath := group.calculateAbsolutePath(relativePath) #计算绝对路径
    	handlers = group.combineHandlers(handlers) #将参数的handlers 和 middleware 的handlers拼接在一起作为一条路由信息,传递给addRoute()函数
    	group.engine.addRoute(httpMethod, absolutePath, handlers)
    	return group.returnObj()
    }
    

     查看group.combineHandlers()函数的实现

    func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
    	finalSize := len(group.Handlers) + len(handlers)
    	if finalSize >= int(abortIndex) {
    		panic("too many handlers")
    	}
    	mergedHandlers := make(HandlersChain, finalSize) #创建一个HandlersChain的数组,然后把group.handlers 和 参数的handlers copy 进去
    	copy(mergedHandlers, group.Handlers)
    	copy(mergedHandlers[len(group.Handlers):], handlers)
    	return mergedHandlers
    }
    

     于是每一条路由的达到,如果根据基树找到这里的HandlersChain,那么就会触发该HandlersChain中所有的func, 至于如何找到的,查看gin 启动流程分析这篇文章。

    这样的中间件设计有什么优缺点呢?

    优点:保证所有的路由信息都会用到中间件。

    缺点:如果中间件并不适用于所有的路由策略,则更改起来比较麻烦,需要在中间件内部根据context做判断。

  • 相关阅读:
    DB2 for Z/os Statement prepare
    Foreign key (referential) constraints on DB2 LUW v105
    复制Informational constraints on LUW DB2 v105
    DB2 SQL Mixed data in character strings
    DB2 create partitioned table
    MVC中使用EF的技巧集(一)
    Asp.Net MVC 开发技巧(二)
    Linq使用技巧及查询示例(一)
    Asp.Net MVC 开发技巧(一)
    Asp.Net MVC Identity 2.2.1 使用技巧(八)
  • 原文地址:https://www.cnblogs.com/Spider-spiders/p/10235385.html
Copyright © 2011-2022 走看看