gin中间件本质是接收gin.context参数且无返回值的函数,必须用指针签名func(gin.context),注册须在路由前调用use(),执行时按序触发c.next()控制流程,需用c.abort()终止请求,body只能读一次,panic捕获需将gin.recovery()置于最外层。

中间件函数必须返回 func(*gin.Context)
Gin 的中间件本质就是一个接受 *gin.Context 参数、无返回值的函数。写成闭包或普通函数都行,但签名不能错——常见错误是漏掉指针符号,写成 func(gin.Context),会导致编译失败或 panic。
- 正确写法:
func(c *gin.Context) { ... } - 闭包形式也常见,比如带配置参数:
func(timeout time.Duration) gin.HandlerFunc { return func(c *gin.Context) { ... } } - 别在中间件里直接调用
c.Next()两次,会重复执行后续 handler,引发逻辑错乱或 panic - 如果中间件要提前终止请求(如鉴权失败),记得调用
c.Abort(),否则流程还会往下走
注册顺序决定执行时机,Use() 和 UseMiddleware() 有区别
Gin 中间件注册分全局和路由组两种方式,行为一致但作用域不同。重点不是“怎么注册”,而是“什么时候生效”:所有 Use() 添加的中间件,会在匹配到路由前按顺序执行;而 c.Next() 是控制权交还给后续中间件或最终 handler 的关键开关。
- 全局注册:
r.Use(mw1, mw2)→ 所有路由都会经过 - 路由组注册:
v1 := r.Group("/api"); v1.Use(authMW)→ 仅该组下路由生效 -
UseMiddleware()是旧版别名,新项目统一用Use()即可,避免混淆 - 注意:
Use()必须在GET/POST等路由注册之前调用,否则不生效
中间件里读取请求体容易出错:c.Request.Body 只能读一次
很多自定义中间件需要解析 JSON 或表单数据(比如日志、签名验证),但直接调用 c.ShouldBindJSON() 或 c.PostForm() 会消耗 Body 流,导致后续 handler 再读就为空。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 解决办法:用
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))重置 Body(Gin v1.9+ 推荐用gin.DefaultWriter配合c.Copy()更安全) - 更稳妥的做法是只在中间件中做轻量检查(如 header、query、path),把完整解析留给业务 handler
- 若必须解析 Body,建议用
c.MustGet("body")+c.Set("body", data)在中间件间传递已解析结构,避免重复解析
调试时看不到中间件 panic?加 gin.Recovery() 要放在最外层
默认 Gin 不捕获中间件里的 panic,一旦发生就会直接崩溃。很多人加了 Recovery() 却发现没起作用,其实是位置错了——它必须是第一个 Use() 的中间件,否则 panic 发生在它之前就逃逸了。
- 正确顺序:
r.Use(gin.Recovery(), loggerMW, authMW) -
Recovery()默认会打印 stack trace 到标准错误,生产环境建议配合自定义 error writer - 不要在中间件里用
defer/recover自己兜底,和Recovery()行为冲突,可能掩盖真实问题
中间件最难的从来不是写法,而是搞清控制流在哪一层被截断、Body 流被谁消费、panic 在哪冒出来又消失在哪。多打几个 fmt.Println("before next") 和 fmt.Println("after next") 比查文档更快定位问题。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










