中间的几个关键对象

  • Engine
  • RouterGroup
  • HandlerFunc
  • Context

Engine

New()Default()
NewDefaultloggerrecover
Engine.Use()RouterGroup
Engine.Run

RouterGroup

type RouterGroup struct {
    Handlers HandlersChain
    basePath string
    engine   *Engine
    root     bool
}
HandlersChain[]HandlerFunc
engine.trees

Engine 包含 RouterGroup, RouterGroup 也会存储 Engine 的地址. 目的是为了, 加点api地址时, 根据当前具体配置的中间件, 再添加engine的路由树. 也就是说, 过程中修改了中间件, 不会影响到已经配置的路由的中间件.

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()
}

注册路由之前, 先将当前的 http 处理方法, 与当前中间件配置合并, 再添加到engine.

RouterGroup

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

创建新的 Group 时候,也会继承旧的 Group, 原先已经设置的 middleware. gin 框架不支持移除中间件的操作,所以在上层设置的中间件的时候一定要确保是公共 common 的


相似的路由捆绑在一块, 路由组写法, 可以做绑定相同中间件处理.

// Simple group: v1
v1 := router.Group("/v1")
{
  v1.POST("/login", loginEndpoint)
  v1.POST("/submit", submitEndpoint)
  v1.POST("/read", readEndpoint)
}

中间的花括号只起美化代码作用, 非必需

HandlerFunc

中间件和请求处理方法的函数签名

type HandlerFunc func(*Context)

处理请求的时候, HandlerFunc 执行完毕 -- 代表该请求处理结束

Context

gin 自己实现的 Context 结构

  • 在中间件中传递参数
  • 控制调用流程
  • 获取参数
  • 返回结果

Context 结构的重要组成部分介绍

type Context struct {
    // 存储URL 参数
    Params   Params

    // 写入请求的结果
    Writer    ResponseWriter

    // 请求的所有处理方法集合
    handlers HandlersChain

    // 当前执行到第几个方法
    index    int8

    // 存储流转于中间件的参数式
    Keys map[string]any
}

在中间件中传递参数

context.Getcontext.Set

流程控制

context.Next()
func (c *Context) Next() {
    c.index++
    for c.index < int8(len(c.handlers)) {
        c.handlers[c.index](c)
        c.index++
    }
}
HandlerFunc

获取参数

context.Param()context.Query()context.QueryMap()context.PostFormMapcontext.Bindbinding:"required"

调用结束

conetxt.JSONjsonBindingcontext.AbortWithStatus()

Cookie 操作

context.Cookie()context.SetCookie()

Middleware

Engine.Use
Use

Next

调用流程图:

middleware 1
| - doing m1 work
| - call Context.Next()
|     - call middleware 2
|       - doing m2 work
|       - call Context.Next()
|         - call middleware 3
|         - - done m3
|       - continue m2 work
|     - - done m2 work
| - coninue m1 work
| - done 
finish
Context.Next()Context.Next()

Abort

Context.Abort()
func (c *Context) Abort() {
    c.index = abortIndex
}

会将函数调用链的index指向一个超大整数 -- 放弃后面所有的处理函数, 但是 调用Abort后, 同一个函数接下的代码还会继续执行

小demo

func main() {
    eg := gin.New()
    eg.Use(m1, m2)
    eg.GET("/greet", m3, greet)
    eg.GET("/abort", abort, m3, greet)
    eg.Run(":8080")

}

func m1(c *gin.Context) {
    fmt.Println("before m1 next")
    c.Next()
    fmt.Println("after m1 next")
}

func m2(c *gin.Context) {
    fmt.Println("\tbefore m2 next")
    c.Next()
    fmt.Println("\tafter m2 next")
}

func m3(c *gin.Context) {
    fmt.Println("\t\tbefore m3 next")
    c.Next()
    fmt.Println("\t\tafter m3 next")
}

func abort(c *gin.Context) {
    fmt.Println("\t\t\tbefore abort")
    c.Abort()
    fmt.Println("\t\t\tafter abort")
}

func greet(c *gin.Context) {
    fmt.Println("\t\t\t\thow are you doing?")
    c.JSON(200, "great")
}
/greet
before m1 next
        before m2 next
                before m3 next
                                how are you doing?
                after m3 next
        after m2 next
after m1 next
/abort
before m1 next
        before m2 next
                        before abort
                        after abort
        after m2 next
after m1 next

其他

gin.H

type gin.H map[string]interface{} 
// 常用于 engine.JSON() 时返回 json 数据

几个问题

Gin 的 context 有什么作用和怎么用?

context 是 Gin 代码请求流转的核心, 存储处理请求的所有必须参数

基本的使用方法:

BindParamNextAbortJSON

Gin 的整体框架流程是怎么样的? 从接受到一个请求再到返回请求中间的流程?

Engine.ServeHTTP

整体流程

nodetreeHandlerChaingin.Contextgin.Contextgin.Context.Writer