Gin框架入门-路由分组和中间件

本文最后更新于:1 年前

一、路由分组

在我们实际的开发应用中我们希望能个各个功能模块的路由进行分组,同一个模块的不同路由带有同样的前缀。

  • 作用:首先是路由更加的清晰 并且我们在针对某一组路由进行中间件权限校验的时候也比较的方便。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
r := gin.Default()
v1 := r.Group("v1")
v1.GET("/test1", func(c *gin.Context) {
c.JSON(200, gin.H{
"msg": "这里是test1",
})
})
v1.GET("/test2", func(c *gin.Context) {
c.JSON(200, gin.H{
"msg": "这里是test2",
})
})
r.Run(":8080")

打开postman选择GET请求,访问http://localhost:8080/v1/test1

二、中间件

1、中间件简介

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


中间件就是作用于两个模块之间的功能软件,可以作为拦截器、记录日志等,比如在前后端开发中

路由——–> 中间件(过滤作用)——–> 控制器

在Gin中,中间件的效果可以简单的概括为:

  • 设置好中间件以后,中间件后面的路由都会使用这个中间件
  • 设置在中间件之前的路由则不会生效

2、定义中间件

Gin中的中间件必须是一个gin.HandlerFunc类型,在自定义中间件函数时,有两种写法:

1
2
3
4
5
6
func middleW() gin.HandlerFunc {
return func(c *gin.Context) {
...
}
}
router.Use(middleW())

或者

1
2
3
4
func middleW(c *gin.Context) {
...
}
router.Use(middleW())

3、注册中间件

在Gin框架中,我们可以注册全局中间件,也可以给单独路由或者路由组注册中间件,可以为路由添加任意数量的中间件。

  • 当我们存在多个中间件的时候,中间件的处理顺序是参考洋葱模型:


简而言之,请求是队列处理,响应则是堆栈处理。

  • 定义中间件代码:
    1
    2
    3
    4
    5
    6
    7
    8
    //声明一个中间件方法
    func middleW() gin.HandlerFunc {
    return func(c *gin.Context) {
    fmt.Println("我在方法前")
    c.Next()
    fmt.Println("我在方法后")
    }
    }

1)注册全局中间件

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
func main() {
r := gin.Default()
//注册一个全局中间件
r.Use(middleW())
r.GET("/test", func(c *gin.Context) {
fmt.Println("我在方法内部")
c.JSON(200, gin.H{
"msg": "成功了",
})
})
r.Run(":8080")
}

打开postman选择GET请求,访问http://localhost:8080/test

2)单独注册某个路由中间件

代码示例:

1
2
3
4
5
6
7
8
9
10
11
func main() {
r := gin.Default()
r.GET("/test", middleW(), func(c *gin.Context) {
fmt.Println("我在方法内部")
c.JSON(200, gin.H{
"msg": "成功了",
})
})
r.Run(":8080")
}

打开postman选择GET请求,访问http://localhost:8080/test

3)注册路由组中间件

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func main() {
r := gin.Default()
//定义一个路由组 并注册中间件
v1 := r.Group("v1").Use(middleW())
v1.GET("/test1", func(c *gin.Context) {
fmt.Println("我在方法内部")
c.JSON(200, gin.H{
"msg": "这里是test1",
})
})
v1.GET("/test2", func(c *gin.Context) {
fmt.Println("我在方法内部")
c.JSON(200, gin.H{
"msg": "这里是test2",
})
})
r.Run()
}

打开postman选择GET请求,访问http://localhost:8080/v1/test1

4、中间件的嵌套

中间件可以嵌套使用,这里有三个Gin框架相关的函数。

1)Next()

表示跳过当前中间件剩余内容, 去执行下一个中间件。 当所有操作执行完之后,以出栈的执行顺序返回,执行中间件的剩余代码。
中间件定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//定义中间件1
func middlewOne() gin.HandlerFunc {
return func(c *gin.Context) {
fmt.Println("我在方法前,我是1")
c.Next()
fmt.Println("我在方法后,我是1")
}
}

//定义中间件2
func middlewTwo() gin.HandlerFunc {
return func(c *gin.Context) {
fmt.Println("我在方法前,我是2")
c.Next()
fmt.Println("我在方法后,我是2")
}
}

func main() {
r := gin.Default()
//使用多个中间件
r.GET("/test", middlewOne(), middlewTwo(), func(c *gin.Context) {
fmt.Println("我在方法内部")
c.JSON(200, gin.H{
"msg": "这里是test1",
})
})
r.Run()
}

打开postman选择GET请求,访问http://localhost:8080/test

2)return

终止执行当前中间件剩余内容,执行下一个中间件。 当所有的函数执行结束后,以出栈的顺序执行返回,但不执行return后的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//定义中间件1
func middlewOne() gin.HandlerFunc {
return func(c *gin.Context) {
fmt.Println("我在方法前,我是1")
c.Next()
fmt.Println("我在方法后,我是1")
}
}

//定义中间件2
func middlewTwo() gin.HandlerFunc {
return func(c *gin.Context) {
fmt.Println("我在方法前,我是2")
return
fmt.Println("我在方法后,我是2")
}
}

//定义中间件3
func middlewThree() gin.HandlerFunc {
return func(c *gin.Context) {
fmt.Println("我在方法前,我是3")
c.Next()
fmt.Println("我在方法后,我是3")
}
}
func main() {
r := gin.Default()
//使用多个中间件
r.GET("/test", middlewOne(), middlewTwo(), middlewThree(), func(c *gin.Context) {
fmt.Println("我在方法内部")
c.JSON(200, gin.H{
"msg": "这里是test1",
})
})
r.Run()
}

打开postman选择GET请求,访问http://localhost:8080/test


3)Abort()

只执行当前中间件, 操作完成后,以出栈的顺序,依次返回上一级中间件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//定义中间件1
func middlewOne() gin.HandlerFunc {
return func(c *gin.Context) {
fmt.Println("我在方法前,我是1")
c.Next()
fmt.Println("我在方法后,我是1")
}
}

//定义中间件2
func middlewTwo() gin.HandlerFunc {
return func(c *gin.Context) {
fmt.Println("我在方法前,我是2")
c.Abort()
fmt.Println("我在方法后,我是2")
}
}

//定义中间件3
func middlewThree() gin.HandlerFunc {
return func(c *gin.Context) {
fmt.Println("我在方法前,我是3")
c.Next()
fmt.Println("我在方法后,我是3")
}
}
func main() {
r := gin.Default()
//使用多个中间件
r.GET("/test", middlewOne(), middlewTwo(), middlewThree(), func(c *gin.Context) {
fmt.Println("我在方法内部")
c.JSON(200, gin.H{
"msg": "这里是test1",
})
})
r.Run()
}

打开postman选择GET请求,访问http://localhost:8080/test

5、中间件注意事项

1)gin默认中间件

gin.Default()默认使用了Logger和Recovery中间件,其中:

Logger中间件将日志写入gin.DefaultWriter,即使配置了GIN_MODE=release。
Recovery中间件会recover任何panic。如果有panic的话,会写入500响应码。
如果不想使用上面两个默认的中间件,可以使用gin.New()新建一个没有任何默认中间件的路由。

2)gin中间件中使用goroutine

当在中间件或handler中启动新的goroutine时,不能使用原始的上下文(c *gin.Context),必须使用其只读副本(c.Copy())。

参考大佬资料:https://blog.csdn.net/qq_49723651/article/details/123694746


Gin框架入门-路由分组和中间件
https://gopherlinzy.github.io/2022/07/18/gin-Group-Middleware/
作者
孙禄毅
发布于
2022年7月18日
许可协议