中间件必须严格遵循func(*gin.context)签名、use()调用须在路由注册前、c.request.body只能读一次且需缓存、gin.recovery()须置于use链首位,否则静默失效。

直接用 go mod init 初始化项目、go get -u github.com/gin-gonic/gin 拉 Gin 就能跑中间件,但真正卡住人的从来不是“怎么装”,而是中间件注册后不执行、c.Next() 调了没反应、c.Request.Body 读完下游就空——这些全是签名、顺序、流控制没对齐导致的。
中间件函数签名必须是 func(*gin.Context)
Gin 对中间件类型校验极严,传错签名会 panic,错误信息类似 panic: interface conversion: interface {} is func(), not gin.HandlerFunc。不是语法错,是类型系统直接拦截。
- ❌ 错误写法:
func(c gin.Context)(漏指针)、func(c *gin.Context, timeout time.Duration)(多参数)、func() gin.HandlerFunc(没调用) - ✅ 正确写法:显式声明为
gin.HandlerFunc类型,或直接写func(c *gin.Context) - 闭包带配置时,返回值必须是
gin.HandlerFunc:func(timeout time.Duration) gin.HandlerFunc,然后r.Use(AuthMiddleware(30*time.Second)) - 别在
Use()里传函数地址:r.Use(myMiddleware)是错的;要传执行结果:r.Use(myMiddleware())
Use() 必须在路由注册之前调用
中间件注册时机错了,等于没注册。Gin 不会回溯补挂,它只对后续注册的路由生效。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- ✅ 正确顺序:
r.Use(recoveryMW, loggerMW, authMW)→ 再r.GET("/user", handler) - ❌ 反例:先写
r.GET(...),再r.Use(...)—— 这个中间件对已注册的路由完全无效 - 路由组同理:
v1 := r.Group("/api/v1"); v1.Use(authMW)必须在v1.GET(...)之前 -
gin.Default()自带Logger和Recovery,但如果你用gin.New(),就得自己手动Use(),漏掉就看不到日志、panic 直接崩溃
读请求体前必须复制或缓存 c.Request.Body
HTTP Body 是单次读取流,中间件一读,后续 handler 调 c.ShouldBindJSON() 就报 invalid character —— 不是解析错,是流已经 EOF 了。
- Gin v1.9+ 推荐用
c.Copy()获取上下文副本做异步处理,但 Body 本身仍不可重放 - 真要解析 Body(比如签名校验),建议只在中间件里做轻量检查(header、query),把完整解析留给业务 handler
- 必须解析时,用
ioutil.ReadAll(c.Request.Body)读出字节,再用bytes.NewBuffer(bodyBytes)包装回c.Request.Body = io.NopCloser(...),但大请求慎用,内存吃紧 - 更稳妥方案:中间件解析后
c.Set("parsed_body", data),下游用c.MustGet("parsed_body")拿,避免重复读
gin.Recovery() 必须放在 Use() 链最前面
它不是“加了就保险”,而是“必须第一个执行”——否则 panic 发生在它上游,根本捕获不到。
- ✅ 正确:
r.Use(gin.Recovery(), authMW, loggerMW) - ❌ 错误:
r.Use(loggerMW, gin.Recovery(), authMW)—— 如果loggerMW里 panic,服务直接挂 -
c.Abort()是终止当前中间件链,不是退出整个请求;c.Next()是移交控制权给下一个中间件或 handler,别重复调用,否则逻辑重入 - 鉴权中间件里记得
c.Set("user", user),否则下游c.Get("user")永远是 nil —— Set 和 Get 必须在同一条链里,且Set要早于Get
中间件真正的复杂点不在写函数,而在理解它是一条严格顺序、单向流动、不可 rewind 的执行链——任何一步签名错、位置错、流操作错,都会让整条链静默失效,而不是报错提醒。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










