Dawn's Blogs

分享技术 记录成长

0%

Gin基本使用 (4) 中间件

Gin中间件

Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。

定义中间件

Gin的中间件必须是一个gin.handlerFunc类型type HandlerFunc func(*Context)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// TimeCost是一个统计请求耗时的中间件
func TimeCost(c *gin.Context) {
start := time.Now()
// 调用该请求的剩余处理程序(中间件)
c.Next()
// 不调用该请求的剩余处理程序
// c.Abort()
// 计算耗时
cost := time.Since(start)
fmt.Printf("Time cost = %v\n", cost)
}

// 可以将中间件定义为闭包的形式,HadlerFunc以函数值返回
func otherMiddleware() gin.HadlerFunc {
// 做一些校验/准备/查询数据库工作
// ....
return func(c *gin.Context) {
/*
一些中间件的处理逻辑
*/
}
}

注册中间件

注册全局中间件

使用Use方法注册全局中间件:

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
r := gin.Default()
// 注册全局中间件
r.Use(TimeCost)
r.GET("/index",func(c *gin.Context) {
// 后端逻辑
c.JSON(http.StatusOK, gin.H{
"status": "OK",
})
})

r.Run(":9090")
}

为某个路由注册中间件

1
2
3
4
5
6
7
// 注册中间件TimeCost
r.GET("/index", TimeCost, func(c *gin.Context) {
// 后端逻辑
c.JSON(http.StatusOK, gin.H{
"status": "OK",
})
})

为路由组注册中间件

在使用Group方法定义路由组时,可以为路由组加上中间件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 方法1:
userGroup := r.Group("/user", TimeCost)
{
userGroup.GET("/index", func(c *gin.Context) { /* ... */ })
// ...
}

// 方法2:
userGroup := r.Group("/user")
userGroup.Use(TimeCost)
{
userGroup.GET("/index", func(c *gin.Context) { /* ... */ })
// ...
}

注意事项

gin默认中间件

gin.Default默认使用了LoggerRecovery中间件:

  • Logger中间件用于记录日志,将日志写入 gin.DefaultWriter
    • 可以设置 gin.DefaultWriter,改变日志的输出
    • 可以设置 gin.DebugPrintRouteFunc,指定日志的输出格式
    • 也可以使用 gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string) 中间件自定义日志输出,会写入日志到 gin.DefaultWriter
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // LoggerWithFormatter 中间件会写入日志到 gin.DefaultWriter
    // 默认 gin.DefaultWriter = os.Stdout
    router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
    // 你的自定义格式
    return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n",
    param.ClientIP,
    param.TimeStamp.Format(time.RFC1123),
    param.Method,
    param.Path,
    param.Request.Proto,
    param.StatusCode,
    param.Latency,
    param.Request.UserAgent(),
    param.ErrorMessage,
    )
    }))
  • Recovery中间件会recover任何的panic错误,如果有panic错误,向客户端返回500响应码

若不使用默认中间件,可以使用gin.New()新建一个没有任何默认中间件的路由。

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

gin中间件中使用goroutine

在中间件中启动新的goroutine时,不能使用原始上下文的c *gin.Context(否则在后续的处理过程中,上下文内容不可控,造成并发不安全),必须使用其只读副本c.Copy()