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 />