gin学习记录-中间件和路由

明日香

6. gin 中间件和路由

把请求的处理函数叫做中间件,且形参必须是*gin.Context 一个请求可以有多个中间件
通过Use()函数来调用中间件

6.1 单个路由和中间件

  • Next() 会将当前中间件前后分离,前面的为请求中间件,后面的为响应中间件
    执行完前面遇到Next()就执行下一个中间件,把响应中间件压入栈中(大概效果是这样)。

  • 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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
type User struct {  
Name string
Age int
}

func _set(c *gin.Context) {
// 设置kv
c.Set("note", "这是一条kv设置")

// 设置结构体数据
c.Set("user", User{
Name: "kjasn",
Age: 11,
})
}

func main() {
router := gin.Default()

// 使用全局中间件 不需要匹配路径,直接用
router.Use(_set)

router.GET("/setVal", func(c *gin.Context) {
// 用Get()接收 形参是key 返回any类型的val和一个bool值表示是否存在key
// 接收单独设置的kv
note, ok := c.Get("note")
if ok {
fmt.Println(note)
}

////////////////////////////////////////////////////////////
// 接收结构体 kv
user, ok := c.Get("user")
if ok {
// 直接打印整个user
fmt.Println(user)
c.JSON(http.StatusOK, gin.H{"data": user})

// 单独打印 需要断言为User类型才能访问User类型的属性
fmt.Println("断言")
_user, ok := user.(User) // 断言成功 _user 接收到user的类型,若失败则为空
if ok {
fmt.Println("姓名:", _user.Name, "年龄:", _user.Age)
c.JSON(http.StatusOK, gin.H{"name": _user.Name, "age": _user.Age})
} else {
fmt.Println("断言失败,不是User类型")
}
}
})

err := router.Run(":80")
if err != nil {
panic(err)
}
}
  • 通过中间件传递数据并接收 key-value
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
type User struct {  
Name string
Age int
}

func _set(c *gin.Context) {
// 设置kv
c.Set("note", "这是一条kv设置")

// 设置结构体数据
c.Set("user", User{
Name: "kjasn",
Age: 11,
})
}

func main() {
router := gin.Default()

// 使用全局中间件 不需要匹配路径,直接用
router.Use(_set)

router.GET("/setVal", func(c *gin.Context) {
// 用Get()接收 形参是key 返回any类型的val和一个bool值表示是否存在key
// 接收单独设置的kv
note, ok := c.Get("note")
if ok {
fmt.Println(note)
}

////////////////////////////////////////////////////////////
// 接收结构体 kv
user, ok := c.Get("user")
if ok {
// 直接打印整个user
fmt.Println(user)
c.JSON(http.StatusOK, gin.H{"data": user})

// 单独打印 需要断言为User类型才能访问User类型的属性
fmt.Println("断言")
_user, ok := user.(User) // 断言成功 _user 接收到user的类型,若失败则为空
if ok {
fmt.Println("姓名:", _user.Name, "年龄:", _user.Age)
c.JSON(http.StatusOK, gin.H{"name": _user.Name, "age": _user.Age})
} else {
fmt.Println("断言失败,不是User类型")
}
}
})

err := router.Run(":80")
if err != nil {
panic(err)
}
}

6.2 路由分组

将一系列的路由放到一个组下,统一管理,为分组路由定义全局中间件同单个路由定义全局中间件一样,用Use()函数

  • 定义一些需要的结构体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  
// 封装响应格式
type Response struct {
Code int `json:"code"`
Date any `json:"date"`
Message string `json:"msg"`
}

type UserInfo struct {
Name string `json:"name"`
Age int `json:"age"`
}

type ArticleInfo struct {
Title string `json:"title"`
Content string `json:"content"`
}
  • 定义中间件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 此处直接内定一些简单的数据
func DisplayUserList(c *gin.Context) {
list := []UserInfo{
{"张三", 23},
{"李四", 45},
}

c.JSON(http.StatusOK, Response{Code: 1, Date: list, Message: "请求成功"})
}

func DisplayArticle(c *gin.Context) {
list := []ArticleInfo {
{"c语言入门到入土", "本书是c语言入门教程,面向入土"},
{"数据库从删库到跑路", "本书教你如何从删库到跑路"},
}

c.JSON(http.StatusOK, Response{Code: 1, Date: list, Message: "请求成功"})
}

func showTest(c *gin.Context) {
fmt.Println("showTest in ")
c.JSON(http.StatusOK, gin.H{"msg": "这是一个用来测试的中间件"})
}
  • 拆分出来,这样也方便单独成包
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
func UserRouterInit(api *gin.RouterGroup) {  
// 用户管理分组 将userManagement作为api下的一个组 同一个组内的请求一般放在一个大括号里
// 发出请求 http://127.0.0.1/api/user-management/user
userManagement := api.Group("user-management") {
// 以下是userManagement分组下的一些请求
userManagement.GET("/user", DisplayUserList)
// ......
}
}

func ArticleRouterInit(api *gin.RouterGroup) {
// 文章管理分组 将article-management作为api下的一个组 与userManagement并列
// 发出请求 http://127.0.0.1/api/article-management/article
article := api.Group("article-management") {
// 以下是article-management分组下的一些请求,就像之前写的一样 此处简写
article.GET("/article", DisplayArticle)
// ..........
}
}

func Test(api *gin.RouterGroup) {
// 为test组注册showTest全局中间件,该组下的子组都会用到这个中间件
test := api.Group("test").Use(showTest) {
test.GET("show1", func(c *gin.Context) {
fmt.Println("in show1")
})
test.GET("show2", func(c *gin.Context) {
fmt.Println("in show2")
})
}
}
  • 在main()中分组然后直接调用拆分出来的包即可
    分组之后,请求当前组下的其他分组需要加上父组的路径作为前缀
    1
    2
    3
    4
    5
    6
    // 分组 
    api := router.Group("api") // 返回一个组

    UserRouterInit(api)
    ArticleRouterInit(api)
    Test(api)

6.3 讲讲gin.Default()和gin.New()

  • gin.New() 是创建一个新的空白引擎,没有添加任何中间件。而**gin.Default()中调用了gin.New()并添加两个中间件 gin.Logger(), gin.Recovery(),这是二者的主要区别。 **

  • gin.Logger() : 记录请求日志:在每次请求到达服务器时,记录请求的信息,包括请求的方法(GET、POST 等)、请求的路径、请求的 IP 地址等。

  • gin.Recovery() : 恢复从错误中恢复:在处理请求时,捕获潜在的异常,比如代码出现了意外的错误或崩溃。它会确保应用不会因为异常而完全崩溃,而是尽量将异常信息记录下来,并返回一个友好的错误响应给客户端 。

1
2
3
// 以下就等同于用gin.Default()  
router := gin.New()
router.Use(gin.Logger(), gin.Recovery())

gin学习记录-中间件和路由
https://kjasn.github.io/2023/08/26/gin学习记录-中间件和路由/
作者
Kjasn
发布于
2023年8月26日
许可协议