1.概述
Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。
2.定义中间件
Gin中的中间件必须是一个gin.HandlerFunc类型。例如我们像下面的代码一样定义一个统计请求耗时的中间
package main import ( "fmt" "github.com/gin-gonic/gin" "net/http" ) func inedxHandler(c *gin.Context) { fmt.Println("index") c.JSON(http.StatusOK, gin.H{ "msg": "index", }) } // 定义一个中间件m1 func m1(c *gin.Context) { fmt.Println("this is m1...") } func main() { r := gin.Default() r.GET("/index", m1, inedxHandler) r.Run(":9090") // 启动server } --------------------------------------------------- 输出: this is m1... index
使用中间件,实现一个计算耗时的程序:
package main import ( "fmt" "github.com/gin-gonic/gin" "net/http" "time" ) func inedxHandler(c *gin.Context) { fmt.Println("index") c.JSON(http.StatusOK, gin.H{ "msg": "index", }) } // 定义一个中间件m1 func m1(c *gin.Context) { fmt.Println("this is m1...") // 计时 start := time.Now() c.Next() // 调用后续的处理函数 // c.Abort() // 阻止调用后续的处理函数 cost := time.Since(start) fmt.Printf("cost:%v\n", cost) fmt.Println("m1 out ...") } func main() { r := gin.Default() r.GET("/index", m1, inedxHandler) r.Run(":9090") // 启动server } ------------------------------------------------- 输出: this is m1... index cost:0s m1 out ...
3.注册中间件
在gin框架中,我们可以为每个路由添加任意数量的中间件。
为全局路由注册
package main import ( "fmt" "github.com/gin-gonic/gin" "net/http" "time" ) func inedxHandler(c *gin.Context) { fmt.Println("index") c.JSON(http.StatusOK, gin.H{ "msg": "index", }) } // 定义一个中间件m1 func m1(c *gin.Context) { fmt.Println("this is m1...") // 计时 start := time.Now() c.Next() // 调用后续的处理函数 // c.Abort() // 阻止调用后续的处理函数 cost := time.Since(start) fmt.Printf("cost:%v\n", cost) fmt.Println("m1 out ...") } func main() { r := gin.Default() r.Use(m1) // 全局注册中间件函数 r.GET("/index", inedxHandler) r.Run(":9090") // 启动server }
两个中间件同时使用:
package main import ( "fmt" "github.com/gin-gonic/gin" "net/http" "time" ) func inedxHandler(c *gin.Context) { fmt.Println("index") c.JSON(http.StatusOK, gin.H{ "msg": "index", }) } // 定义一个中间件m1 func m1(c *gin.Context) { fmt.Println("this is m1...") // 计时 start := time.Now() c.Next() // 调用后续的处理函数 // c.Abort() // 阻止调用后续的处理函数 cost := time.Since(start) fmt.Printf("cost:%v\n", cost) fmt.Println("m1 out ...") } func m2(c *gin.Context) { fmt.Println("m2 in ...") c.Next() fmt.Println("m2 out ...") } func main() { r := gin.Default() r.Use(m1, m2) // 全局注册中间件函数 r.GET("/index", inedxHandler) r.Run(":9090") // 启动server }
注意,两个不同的中间件递归执行
输出:
this is m1... m2 in ... index m2 out ... cost:0s m1 out ...
使用Abort()
package main import ( "fmt" "github.com/gin-gonic/gin" "net/http" "time" ) func inedxHandler(c *gin.Context) { fmt.Println("index") c.JSON(http.StatusOK, gin.H{ "msg": "index", }) } // 定义一个中间件m1 func m1(c *gin.Context) { fmt.Println("this is m1...") // 计时 start := time.Now() c.Next() // 调用后续的处理函数 // c.Abort() // 阻止调用后续的处理函数 cost := time.Since(start) fmt.Printf("cost:%v\n", cost) fmt.Println("m1 out ...") } func m2(c *gin.Context) { fmt.Println("m2 in ...") c.Abort() // 阻止调用后续的处理函数 fmt.Println("m2 out ...") } func main() { r := gin.Default() r.Use(m1, m2) // 全局注册中间件函数 r.GET("/index", inedxHandler) r.Run(":9090") // 启动server }
输出:
this is m1... m2 in ... m2 out ... cost:0s m1 out ...
执行图:
4.实际应用场景初体验
登录检测
func authMiddleware(doCheck bool) gin.HandlerFunc { // 连接数据库 // 其他准备工作 return func(c *gin.Context) { if doCheck { // 存放具体的逻辑 // 是否登录判断 // if 登录用户 // c.Next() // else // c.Abort() } else { c.Next() } } }
如果想要检查用户是否登录:
r.Use(authMiddleware(true)) // 全局注册中间件函数
如果不想要检查:
r.Use(authMiddleware(false)) // 全局注册中间件函数
为路由组注册中间件
shopGroup := r.Group("/shop", StatCost()) { shopGroup.GET("/index", func(c *gin.Context) {...}) ... }
shopGroup := r.Group("/shop") shopGroup.Use(StatCost()) { shopGroup.GET("/index", func(c *gin.Context) {...}) ... }
中间件传值
package main import ( "fmt" "github.com/gin-gonic/gin" "net/http" ) func inedxHandler(c *gin.Context) { fmt.Println("index") name, ok := c.Get("name") // 接受中间件传递的值信息 if !ok { name = "无名氏" } c.JSON(http.StatusOK, gin.H{ "msg": "index", "name": name, }) } func m2(c *gin.Context) { fmt.Println("m2 in ...") c.Set("name", "dahe") // 传递值 c.Next() fmt.Println("m2 out ...") } func main() { r := gin.Default() r.Use(m2) // 全局注册中间件函数 r.GET("/index", inedxHandler) r.Run(":9090") // 启动server } ------------------------------------------------ 浏览器输出:{"msg":"index","name":"dahe"}
5.中间件注意事项
gin默认中间件
gin.Default()默认使用了Logger和Recovery中间件,其中:
Logger中间件将日志写入gin.DefaultWriter,即使配置了GIN_MODE=release。
Recovery中间件会recover任何panic。如果有panic的话,会写入500响应码。
如果不想使用上面两个默认的中间件,可以使用gin.New()新建一个没有任何默认中间件的路由。
gin中间件中使用goroutine
当在中间件或handler中启动新的goroutine时,不能使用原始的上下文(c *gin.Context),必须使用其只读副本(c.Copy())。