gin 是一个 golang 的 http 框架。

0. 运行

  1. httprouter 版本会导致
engine.router.NotFound = engine.handle404

赋值时类型不匹配。需要找旧一些的httprouter版本。

  1. 在跑例子的时候需要引入0.1版,如果已经安装了正式版的,需要引入正确的版本。

2. Engine

engine

engine对象包含了 httprouter 复用器和一个全局中间件列表。这里用到了结构体的 匿名嵌入 , 使得 engine 类可以使用 RouterGroup 类的方法。在下面的函数中 Use() 和 Group() 以及 Handle() 函数跟其他 RouterGroup 的方法都可以以类似继承的方式被 engine 对象使用。

其中 Use() 函数在 RouterGroup 的 []Handler 切片中添加新的中间件,Group() 函数接受路由组前缀字符串 和处理方法切片(变参)。

所以,以下语句实现的效果相同

api := r.Group("api")
api.Use(AuthMiddleware, AuthMiddleware2)

api := r.Group("/api", AuthMiddleware, AuthMiddleware2)

其实中间件和处理函数并没有本质的区别。需要重点看一下 Handle()函数。:

// Handle registers a new request handle and middlewares
// with the given path and method.
// The last handler should be the real handler, the other ones should be middlewares that can and should be shared among different routes.
// See the example code in github.
//
// For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
// functions can be used.
//
// This function is intended for bulk loading and to allow the usage of less
// frequently used, non-standardized or custom methods (e.g. for internal
// communication with a proxy).
func (group *RouterGroup) Handle(method, p string, handlers []HandlerFunc) {
    p = path.Join(group.prefix, p)
    handlers = group.combineHandlers(handlers)
    group.engine.router.Handle(method, p, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
        group.createContext(w, req, params, handlers).Next()
    })
}

3. Context

上面的 Handle() 方法需要用到一个函数 Next()

// Next should be used only in the middlewares.
// It executes the pending handlers in the chain inside the calling handler.
// See example in github.
func (c *Context) Next() {
    c.index++
    s := int8(len(c.handlers))
    for ; c.index < s; c.index++ {
        c.handlers[c.index](c)
    }
}

它是 Context 的方法。Context 对象是 gin 最重要的概念,它允许我们再中间件之间传递变量, 控制流,校验请求的 JSON,渲染 返回的 JSON 等。

Context struct {
    Req      *http.Request
    Writer   http.ResponseWriter
    Keys     map[string]interface{}
    Errors   []ErrorMsg
    Params   httprouter.Params
    handlers []HandlerFunc
    engine   *Engine
    index    int8
}

其中,正如我们上面提到的 Next() 的作用就是控制流。下面的 Get() 和 Set() 方法,通过操作 Contex.Keys 在中间件之间传递变量,其余函数用来校验请求和返回数据。

4. 总结

 gin 作为一个轻量级而又高性能的 golang 框架,通过阅读其 0.1 版本的代码可以看出它的一些设计思路, 除此以外,理解基本的技术实现,对日常使用也很有好处。