gin context和官方context_gin框架分析

kirito​www.yesiare.cn

1 安装

$ go get -u github.com/gin-gonic/gin
  • 使用http.StatusOK之类的常量,要引入net/http

2 使用

官方文档:https://gin-gonic.com/zh-cn/docs

官方例子:https://github.com/gin-gonic/examples

3 框架分析

r := gin.New()

36758b0bb66f512c1ca0f84398d5570c.png

3.1 从r.Run()出发

gin.Engine 就是整个框架的类:

r.Run(":8080")
func (engine *Engine) Run(addr ...string) (err error) { // 参数是监听地址
    defer func() { debugPrintError(err) }()

    address := resolveAddress(addr)
    debugPrint("Listening and serving HTTP on %sn", address)
    err = http.ListenAndServe(address, engine) // 重点在这里
    return
}

gin.Engine实现了net/http包的接口

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

跳转到gin.Engine.ServeHTTP函数:

func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    c := engine.pool.Get().(*Context)
    c.writermem.reset(w)
    c.Request = req
    c.reset()

    engine.handleHTTPRequest(c) // 这个函数里实现了对具体请求的处理

    engine.pool.Put(c)
}

gin.Context也是框架里一个重要的数据结构。

继续往里跳:

func (engine *Engine) handleHTTPRequest(c *Context) {
    // ...

    // Find root of the tree for the given HTTP method
    t := engine.trees
    for i, tl := 0, len(t); i < tl; i++ {
        if t[i].method != httpMethod {
            continue
        }
        root := t[i].root
        // Find route in tree
        // getValue 返回给定路径上注册的handle 路由匹配算法在这个函数实现
        value := root.getValue(rPath, c.Params, unescape)
        if value.handlers != nil {
            c.handlers = value.handlers
            c.Params = value.params
            c.fullPath = value.fullPath
            c.Next() // 注意这个函数一般只在中间件中调用,依次调用注册的函数
            c.writermem.WriteHeaderNow()
            return
        }
        // 没匹配到路由的处理 ...
        // ...
}

root的类型是gin.node,跳到node.getValue方法:

func (n *node) getValue(path string, po Params, unescape bool) (value nodeValue){
    //.. 这个函数通过path匹配路由,把路由上注册的方法通过 nodeValue 结构体返回
}

从这个方法返回到handleHTTPRequest后,把nodeValue的值给Context,然后调用Context.Next()执行用户注册的函数

3.2 从r.GET()出发

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

func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
    return group.handle(http.MethodGet, relativePath, handlers)
}

由此可见,gin.Engine继承了gin.RouterGroup

type RouterGroup struct {
    Handlers HandlersChain
    basePath string
    engine   *Engine
    root     bool
}

gin.RouterGroup又实现了IRouter接口,进而实现IRoutes接口:

type IRouter interface {
    IRoutes
    Group(string, ...HandlerFunc) *RouterGroup
}

type IRoutes interface {
    Use(...HandlerFunc) IRoutes

    Handle(string, string, ...HandlerFunc) IRoutes
    Any(string, ...HandlerFunc) IRoutes
    GET(string, ...HandlerFunc) IRoutes
    POST(string, ...HandlerFunc) IRoutes
    DELETE(string, ...HandlerFunc) IRoutes
    PATCH(string, ...HandlerFunc) IRoutes
    PUT(string, ...HandlerFunc) IRoutes
    OPTIONS(string, ...HandlerFunc) IRoutes
    HEAD(string, ...HandlerFunc) IRoutes

    StaticFile(string, string) IRoutes
    Static(string, string) IRoutes
    StaticFS(string, http.FileSystem) IRoutes
}

继续看GET()做了什么,跳进去:

// 把handler注册到给定方法和路径的路由上
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
    absolutePath := group.calculateAbsolutePath(relativePath)
    handlers = group.combineHandlers(handlers)
    group.engine.addRoute(httpMethod, absolutePath, handlers) // 重点
    return group.returnObj()
}

继续跳:

// 放到路由树上
func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
    // ...
    debugPrintRoute(method, path, handlers)
    root := engine.trees.get(method)
    if root == nil {
        root = new(node)
        root.fullPath = "/"
        engine.trees = append(engine.trees, methodTree{method: method, root: root})
    }
    root.addRoute(path, handlers)
}

至此成功注册了一个GET方法下的路由

3.3 从r.Group r.Use出发

newGroup := r.Group("/")

func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
    return &RouterGroup{
        Handlers: group.combineHandlers(handlers), // 中间件
        basePath: group.calculateAbsolutePath(relativePath),
        engine:   group.engine,
    }
}

很简单,就是创建了一个RouterGroup对象返回

再来看r.Use

r.Use(gin.Logger())
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
    engine.RouterGroup.Use(middleware...) // 中间件是被注册到 RouterGroup 对象上的
    engine.rebuild404Handlers()
    engine.rebuild405Handlers()
    return engine
}

继续跳:

func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
    group.Handlers = append(group.Handlers, middleware...)
    return group.returnObj()
}

由此可见,RouterGroup.Handlers 保存的就是中间件的handle

注册一个中间件非常简单,下面会说当请求来到时,中间件如何与用户注册的handle一起被执行

3.4 请求过程

中间件和用户注册的路由handle是如何被执行的?

现有RouterGroup,再有注册中间件,然后才有用户注册路由,前两个上面说了,代码很简单

所以还是从r.GET()出发,再看一遍

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

func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
    return group.handle(http.MethodGet, relativePath, handlers)
}

func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
    absolutePath := group.calculateAbsolutePath(relativePath)
    handlers = group.combineHandlers(handlers) // 重点
    group.engine.addRoute(httpMethod, absolutePath, handlers) // 添加路由
    return group.returnObj()
}

一路跳到RouterGroup.handle 里的group.combineHandlers(handlers)

// 这个函数把用户注册的handle和中间件的handle组合起来并返回
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)
    copy(mergedHandlers, group.Handlers)
    copy(mergedHandlers[len(group.Handlers):], handlers)
    return mergedHandlers
}

到这里就很明确了,中间件的handle和用户注册的handle被一起放进了路由树里,中间件在前,用户注册在后

然后再从r.Run来看:

func (engine *Engine) Run(addr ...string) (err error) {
    // ... 
    err = http.ListenAndServe(address, engine)
    return
}

// Engine实现了http.Handler接口,当请求过来时,会调用如下函数
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    c := engine.pool.Get().(*Context)
    c.writermem.reset(w)
    c.Request = req
    c.reset()

    engine.handleHTTPRequest(c)

    engine.pool.Put(c)
}

func (engine *Engine) handleHTTPRequest(c *Context) {
    // ...

    // Find root of the tree for the given HTTP method
    t := engine.trees
    for i, tl := 0, len(t); i < tl; i++ {
        if t[i].method != httpMethod {
            continue
        }
        root := t[i].root
        // Find route in tree
        value := root.getValue(rPath, c.Params, unescape) // 拿到路由上注册的函数
        if value.handlers != nil {
            c.handlers = value.handlers
            c.Params = value.params
            c.fullPath = value.fullPath
            c.Next() // 调用所有
            c.writermem.WriteHeaderNow()
            return
        }
        // ...
}

中间件的handle和用户注册的handle被一起放进了路由树里,所以获取的时候也是一起被获取的

然后在Context.Next里被按照先后顺序调用

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值