中间件
中间件的理论概念十分繁琐,但gin是面向web服务器中间件,所以限定在这个范围里时,可以简单理解为,在数据传输过程中(这个数据传输过程主要为客户端和服务器端之间进行数据传输)数据会经过一个又一个函数处理,这些函数可以视为中间件。
在后端代码中,中间件通常适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。
接下来举一个例子:
func AccountVer(c *gin.Context){
if(false){
c.Abort()
}
fmt.Println("用户验证通过")
}
func main(){
router := gin.Default()
router.GET("/", AccountVer, func(c *gin.Context) {
fmt.Println("index ...")
c.JSON(200, gin.H{"msg": "响应数据"})
})
router.Run(":8080")
}
上面的代码中AccountVer函数就可以视为一个中间件,其功能是检测用户账户是否可用。实际上,对于 gin 来说,可以添加多个函数,其功能都可以视为中间件。
中间件拦截
对于AccountVer函数中的Abort函数,它是一个必须要了解的函数,作用是拦截中间件。具体的来说,就是当该函数执行后,后续所有的中间件都不会再执行了。
例如,我们注册多个中间件:
func m1(c *gin.Context){
c.Abort()
}
func m2(c *gin.Context){
fmt.Println("m2")
}
func m3(c *gin.Context){
fmt.Println("m3")
}
func main(){
router := gin.Default()
router.GET("/", m1, m2, m3)
router.Run(":8080")
}
上述代码最后只会执行m1函数,之后的所有中间件是不执行的,因为在m1中就因为Abort函数而中断了。
中间件放行
与Abort函数相对的,gin中也有直接跳转到下一个中间件执行的函数,也就是中间件放行,该函数就是Next。
func m1(c *gin.Context){
fmt.Println("m1")
c.Next()
fmt.Println("m1")
}
func m2(c *gin.Context){
fmt.Println("m2")
c.Next()
fmt.Println("m2")
}
func m3(c *gin.Context){
fmt.Println("m3")
c.Next()
fmt.Println("m3")
}
func main(){
router := gin.Default()
router.GET("/", m1, m2, m3)
router.Run(":8080")
}
上述代码执行后,会输出:
m1
m2
m3
是因为两个Println函数没有全部被执行,在执行完一个之后就会因为Next函数立马跳转到下一个中间件。
全局中间件
我在开局曾提到,对于后端来说,用到中间件的地方通常是需要所有页面都会配置的功能,如账号验证。那么该如何做才能一键让所有网页都配置上中间件呢?使用Use函数:
func AccountVer(c *gin.Context){
if(false){
c.Abort()
}
fmt.Println("用户验证通过")
}
func main(){
router := gin.Default()
router.Use(AccountVer)
router.GET("/", func(c *gin.Context) {
fmt.Println("index ...")
c.JSON(200, gin.H{"msg": "响应数据"})
})
router.Run(":8080")
}
如上,这样就全局配置了。当然了,确实有些网页不需要验证账号之类,所以全局配置中间件不一定好用,这时我们就需要接触路由分组配置了。
路由分组配置中间件
什么是路由分组?对于网页来说,常有一系列的网页是同样逻辑下的东西,比如用户信息,个人信息,账号安全之类。在上网时,可能会发现他们通常会有相同的前缀:比如/user/settings、/user/msg之类。他们这个相同的前缀的意思就是他们在同一个路由组中。
那么gin的路由组该如何配置?这需要一个函数Group:
func main() {
router := gin.Default()
r := router.Group("/user")
r.GET("/msg", func(c *gin.Context) {
fmt.Println("index ...")
c.JSON(200, gin.H{"msg": "响应数据"})
})
router.Run(":8080")
}
上述代码中就新建了一个路由分组user。
接下来就可以开始学习怎么对路由分组配置中间件了,同样也是使用Use函数,但是不是同一个对象使用它:
...
r.Use(AccountVer)
...
是的,只需要对应分组使用Use函数就可以做到。当然也可以在创建分组时直接使用Use函数:
...
r := router.Group("/user").Use(AccountVer)
...
达到的效果是一样的,可以根据自己的代码习惯选择。
中间件数据传递
可以发现的是,中间件的执行是一个接着一个的队列形式,那么可不可以让前一个中间件为后一个中间件发送数据呢?答案是可以,这需要用到Set函数和Get函数:
func AccountVer(c *gin.Context){
c.Set("flag","YES")
fmt.Println("用户验证通过")
}
func main(){
router := gin.Default()
router.GET("/", AccountVer, func(c *gin.Context) {
flag,ok := c.Get("flag")
fmt.Println("index ...")
c.JSON(200, gin.H{"msg": "响应数据"})
})
router.Run(":8080")
}
上述代码就完成了一个简单的数据传输功能,不难发现gin所支持的数据传输是一个简单的key-value格式数据。
而Set函数和Get函数一个是负责创建需要传输的数据,一个是负责读取数据。
gin.Default()
其实 gin 本身就包含了一些中间件,这些就藏在gin.Default(),主要的作用就是在控制台打印输出哪个 IP 连接了哪个网址,返回什么之类的 log 数据,如果你不想使用官方给的,想自己写这个信息来 log ,那么就不能使用gin.Default()而是gin.New(),其他函数和正常写一样。
func main(){
router := gin.New()
router.GET("/", func(c *gin.Context) {
fmt.Println("text")
c.JSON(200, gin.H{"msg": "响应数据"})
})
router.Run(":8080")
}
这时你就会发现控制台里面没有相关 log 信息了。