1. 什么是中间件
Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数,这个钩子函数就叫中间件。当一个路由使用了某一个(或几个)中间件时,它将会在调用后续 HandlerFunc 前先调用中间件。其实,广义上来看,路由中的任意 HandlerFunc 都可以称作是中间件。不过通常,我们会给多个不同路由使用同一个HandlerFunc,且都在其他HandlerFunc之前,则此时一般将该中间件独立出来。<br />中间件最常见的一种功能就是权限验证,即:当路由调用 func1 时,系统需要知道该用户有没有权限使用该路由,因此需要在进入func1之前进行验证,这就需要中间件的使用。
r := gin.Default()r.POST("/index",validate,func1)
上述代码中,validate 就是我们用于验证权限的中间件。不过,当该中间件被多个路由使用时,会造成代码冗余,因此我们就需要用到 Hook,使某一个路由组都默认使用该中间件。这里要用到 r.User(func) 方法
router := gin.Default()r := router.Group("/home")r.User(validate) // validate为用于验证的中间件{r.GET("/index",func1)r.POST("/login",func2)...// 这些路由在调用funcx之前,都会先进入validate中间件}
除此之外,中间件还常用于 登录认证、数据分页、记录日志、统计耗时 等业务
2. 定义中间件
Gin中的中间件必须是一个 gin.HandlerFunc 类型。最基本的,它可以是一个 HandlerFunc,即以 *gin.Context 为入参的函数。不过绝大多数我们都是使用闭包作为中间件,返回一个HandlerFunc。如:
// StatCost 是一个统计耗时请求耗时的中间件func StatCost() gin.HandlerFunc {return func(c *gin.Context) {start := time.Now()c.Set("name", "小王子") // 可以通过c.Set在请求上下文中设置值,后续的处理函数能够取到该值// 调用该请求的剩余处理程序c.Next()// 不调用该请求的剩余处理程序// c.Abort()// 计算耗时cost := time.Since(start)log.Println(cost)}}
但是,要注意的是,当使用闭包作为中间件式,引用该中间件的语法格式和引用HandlerFunc的语法格式有些不同:
func StatCost() gin.HandlerFunc {return func(c *gin.Context) {...}}func Test(c *gin.Context){...}func main() {r := gin.New()r.Use(StatCost()) // 闭包作为中间件时要加()r.Usr(Test) // 函数不能加}
3. 跨中间件操作
Gin的中间件中有四个方法用于跨中间件操作,分别为:c.Next()、c.Abort()、c.Set()、c.Get()
3.1 c.Next()
当一个中间件执行到 c.Next() 时,它会先停止执行自身接下来的代码,而是转到下一个中间件中执行。当下一个中间件执行完后,将返回自身执行未执行的代码。执行完后,下一个中间件不再执行。
举个例子,该操作可以用来获取某一个中间件的运行实践:
func func1(c *gin.Context) {startTime := time.Now()fmt.Println(startTime)c.Next()spendTime := time.Since(startTime)fmt.Println(spendTime)}func func2(c *gin.Context) {fmt.Println("func2 start")var str = "have a nice day!"var test []byte = []byte(str)for index,_ := range test {index ++}}func main() {r:= gin.Default()r.GET("/time",func1,func2)r.Run(":8080")}
访问 localhost:8080/time 测试结果如下所示<br /><br />其内部机制,和递归很像,总结一下就是下图这样的原理:<br />
3.2 c.Abort()
顾名思义,该操作将停止执行后续的中间件。
func func1(c *gin.Context) {fmt.Println("func1 start")c.Abort()fmt.Println("func1 continue")}func func2(c *gin.Context) {fmt.Println("func2 start")}func main() {r:= gin.Default()r.GET("/test",func1,func2)r.Run(":8080")}
3.3 c. Set() 和 c.Get()
这两个方法一般配套使用,c.Set 用于在上一个中间件定义一个字段(key-value),c.Get 用于在接下来的中间件里调用该字段。
c.Set 有两个入参,一个为string型的key,一个为作为value的空接口
c.Get 只用一个入参,string型的key,但又两个返回值,第二个为判断该key是否存在的bool值
举个例子:
func func1(c *gin.Context) {c.Set("username","小王子")}func func2(c *gin.Context) {username,exist := c.Get("username")if exist {log.Println(username)}}func main() {r:= gin.Default()r.GET("/test",func1,func2)r.Run(":8080")}
测试结果(发了3次请求)<br />
