Gin中间件
中间件介绍
Gin 框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函 数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、 记录日志、耗时统计等
通俗的讲:中间件就是匹配路由前和匹配路由完成后执行的一系列操作
局部中间件
初始中间件
Gin 中的中间件必须是一个 gin.HandlerFunc 类型,配置路由的时候可以传递多个 func 回调函 数,最后一个 func 回调函数前面触发的方法都可以称为中间件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package main import ( "fmt" "github.com/gin-gonic/gin" ) func initMiddleware(c *gin.Context) { fmt.Println( "我是一个中间件" ) } func main() { r := gin.Default() // 把中间件函数加在视图函数之前 r.GET( "/index" ,initMiddleware, func (c *gin.Context) { c.String(200, "首页" ) }) r.GET( "/home" ,initMiddleware, func (c *gin.Context) { c.String(200, "home" ) }) r.Run() } |
c.Next()
中间件里面加上 ctx.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 | package main import ( "fmt" "github.com/gin-gonic/gin" "time" ) // 写一个中间件,统计视图函数运行时间 func totalTime(c *gin.Context) { start:=time.Now() c.Next() //继续往后执行 end:=time.Now() fmt.Println( "视图函数运行时间为:" ,end.Sub(start)) } func main() { r := gin.Default() // 把中间件函数加在视图函数之前 r.GET( "/index" ,totalTime, func (c *gin.Context) { time.Sleep(time.Second*2) c.String(200, "首页" ) }) r.Run() } |
多个中间件执行顺序
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 | package main import ( "fmt" "github.com/gin-gonic/gin" "time" ) func initMiddleware(c *gin.Context) { fmt.Println( "我是第一个中间件开始" ) c.Next() //继续往后执行 fmt.Println( "我是第一个中间件结束" ) } // 写一个中间件,统计视图函数运行时间 func totalTime(c *gin.Context) { fmt.Println( "我是第二个中间件开始" ) start := time.Now() c.Next() //继续往后执行 end := time.Now() fmt.Println( "视图函数运行时间为:" , end.Sub(start)) fmt.Println( "我是第二个中间件结束" ) } func main() { r := gin.Default() // 把中间件函数加在视图函数之前 r.GET( "/index" , initMiddleware, totalTime, func (c *gin.Context) { fmt.Println( "我是视图函数" ) c.String(200, "首页" ) }) r.Run() } |
1 2 3 4 5 6 | 我是第一个中间件开始 我是第二个中间件开始 我是视图函数 视图函数运行时间为: 79.142µs 我是第二个中间件结束 我是第一个中间件结束 |
c.Abort()
Abort 是终止的意思, c.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 | package main import ( "fmt" "github.com/gin-gonic/gin" ) func initMiddleware1(c *gin.Context) { fmt.Println( "我是第一个中间件开始" ) fmt.Println( "我是第一个中间件结束" ) } func initMiddleware2(c *gin.Context) { fmt.Println( "我是第二个中间件开始" ) if c.FullPath() == "/index" { c.Abort() // 如果是 index请求,直接结束 } fmt.Println( "我是第二个中间件结束" ) } func main() { r := gin.Default() // 把中间件函数加在视图函数之前 r.GET( "/index" , initMiddleware1, initMiddleware2, func (c *gin.Context) { fmt.Println( "我是视图函数-index" ) c.String(200, "首页" ) }) r.GET( "/home" , initMiddleware1, initMiddleware2, func (c *gin.Context) { fmt.Println( "我是视图函数-home" ) c.String(200, "home" ) }) r.Run() } |
1 2 3 4 5 6 7 8 9 10 11 12 | // 1 访问 /home 我是第一个中间件开始 我是第一个中间件结束 我是第二个中间件开始 我是第二个中间件结束 我是视图函数-home // 2 访问 /index 我是第一个中间件开始 我是第一个中间件结束 我是第二个中间件开始 我是第二个中间件结束 |
全局中间件
所有请求都经过此中间件
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 | package main import ( "fmt" "github.com/gin-gonic/gin" ) func initMiddleware(ctx *gin.Context) { fmt.Println( "全局中间件 通过 r.Use 配置" ) // 调用该请求的剩余处理程序 ctx.Set( "name" , "lqz" ) //设置值 ctx.Next() } func main() { r := gin.Default() r.Use(initMiddleware) // 把中间件函数加在视图函数之前 r.GET( "/index" , func (c *gin.Context) { fmt.Println( "我是视图函数-index" ) fmt.Println(c.Get( "name" )) //取 c.String(200, "首页" ) }) r.Run() } |
在路由分组中配置中间件
方式一:
1 2 3 4 5 6 7 | shopGroup := r.Group( "/shop" ,initMiddleware) { shopGroup.GET( "/index" , func (c *gin.Context) { ... }) ... } |
方式二:
1 2 3 4 5 6 7 8 | shopGroup := r.Group( "/shop" ) shopGroup.Use(initMiddleware) { shopGroup.GET( "/index" , func (c *gin.Context) { ... }) ... } |
中间件注意事项
gin默认中间件
gin.Default()默认使用了 Logger 和 Recovery 中间件,其中:
Logger 中间件将日志写入 gin.DefaultWriter,即使配置了 GIN_MODE=release。
Recovery 中间件会 recover 任何 panic。如果有 panic 的话,会写入 500 响应码。
如果不想使用上面两个默认的中间件,可以使用 gin.New()新建一个没有任何默认中间件的 路由。
gin中间件中使用 goroutine
当在中间件或 handler 中启动新的 goroutine 时,不能使用原始的上下文(c *gin.Context), 必须使用其只读副本(c.Copy())
1 2 3 4 5 6 7 8 9 10 | r.GET( "/" , func (c *gin.Context) { cCp := c.Copy() go func () { // simulate a long task with time.Sleep(). 5 seconds time.Sleep(5 * time.Second) // 这里使用你创建的副本 fmt.Println( "Done! in path " + cCp.Request.URL.Path) }() c.String(200, "首页" ) }) |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具