爱摸鱼的Demon
首页
前端知识
后端技术
工程实践
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

爱摸鱼的Demon

我的地盘,欢迎光临。
首页
前端知识
后端技术
工程实践
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • C#

  • Golang

    • Go

    • Gin

      • Gin介绍
      • Gin中的Cookie与Session
      • Gin中间件
    • GORM

  • 后端技术
  • Golang
  • Gin
DemonW-X
2025-09-29

Gin中间件

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

通俗的讲:中间件就是匹配路由前和匹配路由完成后执行的一系列操作。

# 1)路由中间件

Gin中的中间件必须是一个gin.HandlerFunc类型,配置路由的时候可以传递多个func回调函数,最后一个func回调函数前面触发的方法都可以称为中间件。

// 路由中间件
func initMiddleware(c *gin.Context) {
    fmt.Println("aaaaa")
}
func main() {
    r := gin.Default()
    //自定义模板函数,函数只能放在加载模板前
    r.SetFuncMap(template.FuncMap{ //"html/template"包
       "UnixToTime": UnixToTime,
    })
    //加载模板 放在配置路由上面
    r.LoadHTMLGlob("templates/**/*")
    //配置静态web目录 第一个参数表示路由 第二个参数表示映射目录
    r.Static("/static", "./static")
    //前台路由
    //routers.InitDefaultRouters(r)
    //routers.InitApiRouters(r)
    //routers.InitUserRouters(r)
    r.GET("/", initMiddleware, func(c *gin.Context) {
       c.String(http.StatusOK, "gin首页")
    })
    r.GET("/news", initMiddleware, func(c *gin.Context) {
       c.String(http.StatusOK, "新闻首页")
    })
    r.GET("/login", initMiddleware, func(c *gin.Context) {
       c.String(http.StatusOK, "login")
    })
    r.Run()
}
/* 输出结果 (以http://localhost:8080/为例)
    终端:app         | aaaaa
    接口:gin首页
*/
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

# 2)Gin中间件

1、ctx.Next() :调用请求该请求的剩余处理程序。

func initMiddleware(c *gin.Context) {
    start := time.Now().UnixNano()
    fmt.Println("1-I'm initMiddleware")
    //调用该请求的剩余处理程序
    c.Next()

    fmt.Println("2-I'm initMiddleware")
    end := time.Now().UnixNano()
    fmt.Println("3-time:", end-start)
}
func main() {
    r := gin.Default()
    //自定义模板函数,函数只能放在加载模板前
    r.SetFuncMap(template.FuncMap{ //"html/template"包
       "UnixToTime": UnixToTime,
    })
    //加载模板 放在配置路由上面
    r.LoadHTMLGlob("templates/**/*")
    //配置静态web目录 第一个参数表示路由 第二个参数表示映射目录
    r.Static("/static", "./static")
    //前台路由
    //routers.InitDefaultRouters(r)
    //routers.InitApiRouters(r)
    //routers.InitUserRouters(r)
    r.GET("/", initMiddleware, func(c *gin.Context) {
       fmt.Println("I'm index")
       //time.Sleep(time.Second)
       c.String(http.StatusOK, "gin首页")
    })
    r.Run()
}
/*输出结果
    终端:3-time: 0
    接口:gin首页
*/
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

多个中间件调用方式:

func initMiddlewareOne(c *gin.Context) {
    fmt.Println("1-I'm initMiddlewareOne")
    //调用该请求的剩余处理程序
    c.Next()
    fmt.Println("2-I'm initMiddlewareOne")
}
func initMiddlewareTwo(c *gin.Context) {
    fmt.Println("1-I'm initMiddlewareTwo")
    //调用该请求的剩余处理程序
    c.Next()
    fmt.Println("2-I'm initMiddlewareTwo")
}
func main() {
    r := gin.Default()
    //自定义模板函数,函数只能放在加载模板前
    r.SetFuncMap(template.FuncMap{ //"html/template"包
       "UnixToTime": UnixToTime,
    })
    //加载模板 放在配置路由上面
    r.LoadHTMLGlob("templates/**/*")
    //配置静态web目录 第一个参数表示路由 第二个参数表示映射目录
    r.Static("/static", "./static")
    //前台路由
    //routers.InitDefaultRouters(r)
    //routers.InitApiRouters(r)
    //routers.InitUserRouters(r)
    r.GET("/admin", initMiddlewareOne, initMiddlewareTwo, func(c *gin.Context) {
       fmt.Println("I‘m admin index")
       c.String(http.StatusOK, "admin首页")
    })
    r.Run()
}
/*输出结果
    终端:
    1-I'm initMiddlewareOne
    1-I'm initMiddlewareTwo
    I‘m admin index
    2-I'm initMiddlewareTwo
    2-I'm initMiddlewareOne

    接口:admin首页
*/
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

2、ctx.Abort() :终止调用请求该请求的剩余处理程序。

func initMiddlewareAbort(c *gin.Context) {
    start := time.Now().UnixNano()
    fmt.Println("1-I'm initMiddleware")
    //调用该请求的剩余处理程序
    c.Abort()

    fmt.Println("2-I'm initMiddleware")
    end := time.Now().UnixNano()
    fmt.Println("3-time:", end-start)
}

func main() {
    r := gin.Default()
    //自定义模板函数,函数只能放在加载模板前
    r.SetFuncMap(template.FuncMap{ //"html/template"包
       "UnixToTime": UnixToTime,
    })
    //加载模板 放在配置路由上面
    r.LoadHTMLGlob("templates/**/*")
    //配置静态web目录 第一个参数表示路由 第二个参数表示映射目录
    r.Static("/static", "./static")
    //前台路由
    //routers.InitDefaultRouters(r)
    //routers.InitApiRouters(r)
    //routers.InitUserRouters(r)
    r.GET("/news", initMiddlewareAbort, func(c *gin.Context) {
       c.String(http.StatusOK, "新闻首页")
    })
    r.Run()
}
/*输出结果
    终端:3-time: 0
    接口:
*/
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

# 3)全局中间件

func initMiddleware(c *gin.Context) {
    start := time.Now().UnixNano()
    fmt.Println("1-I'm initMiddleware")
    //调用该请求的剩余处理程序
    c.Next()

    fmt.Println("2-I'm initMiddleware")
    end := time.Now().UnixNano()
    fmt.Println("3-time:", end-start)
}
func initMiddlewareOne(c *gin.Context) {
    fmt.Println("1-I'm initMiddlewareOne")
    //调用该请求的剩余处理程序
    c.Next()
    fmt.Println("2-I'm initMiddlewareOne")
}
func initMiddlewareTwo(c *gin.Context) {
    fmt.Println("1-I'm initMiddlewareTwo")
    //调用该请求的剩余处理程序
    c.Next()
    fmt.Println("2-I'm initMiddlewareTwo")
}
func main() {
    r := gin.Default()
    //自定义模板函数,函数只能放在加载模板前
    r.SetFuncMap(template.FuncMap{ //"html/template"包
       "UnixToTime": UnixToTime,
    })
    //加载模板 放在配置路由上面
    r.LoadHTMLGlob("templates/**/*")
    //配置静态web目录 第一个参数表示路由 第二个参数表示映射目录
    r.Static("/static", "./static")
    //全局中间件
    r.Use(initMiddleware, initMiddlewareOne, initMiddlewareTwo)
    r.GET("/", func(c *gin.Context) {
       fmt.Println("I'm index")
       //time.Sleep(time.Second)
       c.String(http.StatusOK, "gin首页")
    })
    r.Run()
}
/*输出结果
    终端:
    1-I'm initMiddleware
    1-I'm initMiddlewareOne
    1-I'm initMiddlewareTwo
    I'm index
    2-I'm initMiddlewareTwo
    2-I'm initMiddlewareOne
    2-I'm initMiddleware
    
    接口:gin首页
*/
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

# 4)路由分组

func InitMiddleware(c *gin.Context) {
    //判断用户是否登录
    fmt.Println(time.Now())
    fmt.Println(c.Request.URL)
}

func InitApiRouters(r *gin.Engine) {
    apiRouters := r.Group("/api", middlewares.InitMiddleware)
    {
       apiRouters.GET("/", api.ApiController{}.Index)
       apiRouters.GET("/add", api.ApiController{}.Add)
       apiRouters.GET("/update", api.ApiController{}.Update)
       apiRouters.GET("/delete", api.ApiController{}.Delete)
       apiRouters.GET("/select", api.ApiController{}.Select)
    }
}
/*输出结果
    终端:
    2025-06-09 19:01:33.0349342 +0800 CST m=+47.332205901
    /api/
    
    接口:api接口
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 5)中间件-控制器、中间件-中间件数据共享

func UserMiddleware(c *gin.Context) {
    //判断用户是否登录
    c.Set("username", "zhangsan")
}
func InitUserRouters(r *gin.Engine) {
    userRouters := r.Group("/user", middlewares.UserMiddleware)
    {
       userRouters.GET("/", user.UserController{}.Index)
       userRouters.GET("/add", user.UserController{}.Add)
       userRouters.GET("/update", user.UserController{}.Update)
       userRouters.GET("/delete", user.UserController{}.Delete)
       userRouters.GET("/select", user.UserController{}.Select)
    }
}
func (con UserController) Index(c *gin.Context) {
    //c.String(http.StatusOK, "user接口")
    con.Success(c)
}
func (con UserController) Add(c *gin.Context) {
    c.String(http.StatusOK, "user接口--增加 -- 继承")
}

func (con UserController) Update(c *gin.Context) {
    c.String(http.StatusOK, "user接口--修改 -- 继承")
}
func (con UserController) Delete(c *gin.Context) {
    c.String(http.StatusOK, "user接口--删除 -- 继承")
}
func (con UserController) Select(c *gin.Context) {
    username, _ := c.Get("username") //空接口类型
    fmt.Println(username)
    v, ok := username.(string)
    if ok == true {
       c.String(http.StatusOK, "user接口--查询 -- 继承--"+v)

    } else {
       c.String(http.StatusBadRequest, "获取用户数据失败!")
    }
}
/*输出结果
    user接口--查询 -- 继承--zhangsan
*/
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

# 6)gin.Default()中的默认中间件

gin.Default()中含有默认中间件Logger()和Recovery()两个中间件。若不想使用的话可以修改为gin.New()默认不创建路由。

Logger()中间件将日志写入gin.DefaultWriter,即配置了GIN_MODE=release

Recovery()中间件回recover任何panic。如果有panic,会写入500响应码

注意:

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

原因:

1)c.Copy() 的作用

调用 c.Copy() 会创建一个 只读、安全的副本,其中包含:

  • 请求的必要数据(如 URL、Header、Method、Path 等)。

  • 与并发无关的数据(不包括响应写入器、Body、上下文变量等)。

副本不会被回收,可以安全地在 goroutine 中使用。

2)*gin.Context 是复用的

Gin 为了提升性能,会通过对象池(sync.Pool)来复用 *gin.Context 对象。也就是说,请求处理完毕后,这个 c 会被回收并复用用于下一次请求。

如果你在 goroutine 中异步地访问 c,而此时主请求已返回,c 可能已经被其他请求复用了,导致读取到的是错误或被修改的数据,甚至引发数据竞态(data race)。

3)goroutine 是异步执行的

当你在 handler 中启动一个 goroutine 时,该 goroutine 的执行时间是不确定的,可能晚于主 handler 的返回时间。

一旦主 handler 返回,c 可能就失效了。

此时 goroutine 中访问 c.Request.URL.Path 等字段,就有可能访问到已经被回收或复用的内存。

编辑 (opens new window)
Gin中的Cookie与Session
Gorm初始化

← Gin中的Cookie与Session Gorm初始化→

最近更新
01
Gorm之事务
11-13
02
Gorm之关联进阶版
11-13
03
Gorm之根据外键关联表
11-13
更多文章>
Theme by Vdoing | Copyright © 2022-2025 爱摸鱼的Demon | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式