5.1 中间件相关名词
- 中间件(Middleware)
- 在业务 handler 前后执行的
gin.HandlerFunc,用于横切关注点:日志、鉴权、限流、CORS、恢复 panic。 - handler 链(Handlers Chain)
- 一次请求匹配到的所有中间件 + 最终 handler 组成的有序切片,Gin 依次执行。
- 洋葱模型(Onion Model)
- 中间件像洋葱层层包裹:
c.Next()之前是「进入」逻辑,之后是「返回」逻辑,形成 A→B→handler→B→A 的执行顺序。 - c.Next()
- 显式调用链中的下一个 handler;调用后的代码在内层全部执行完再运行(用于计时、后置处理)。
- c.Abort()
- 终止后续 handler 的执行(当前函数体仍会跑完),常配合鉴权失败时使用。
- c.Set / c.Get
- 在中间件与 handler 之间通过 Context 传递值(如把解析出的用户 ID 存进去)。
- Use()
- 注册中间件的方法,可挂在 Engine(全局)、Group(分组)或单条路由。
5.2 中间件的本质
中间件就是一个 func(c *gin.Context)。它的行为分两半,以 c.Next() 为分界:
func Timing() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now() // ① 进入:前置逻辑
c.Next() // ② 执行链中后续 handler
cost := time.Since(start) // ③ 返回:后置逻辑
log.Printf("%s %s cost=%v status=%d",
c.Request.Method, c.Request.URL.Path, cost, c.Writer.Status())
}
}
5.3 洋葱模型执行顺序
r.Use(A(), B()) // 顺序注册 A、B
r.GET("/x", handler)
// 执行顺序(洋葱):
// A 前置 -> B 前置 -> handler -> B 后置 -> A 后置
//
// ┌───────── A ─────────┐
// │ ┌────── B ──────┐ │
// │ │ handler │ │
// │ └───────────────┘ │
// └─────────────────────┘
5.4 c.Next 与 c.Abort
func AuthRequired() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
if token == "" {
// AbortWithStatusJSON = 写响应 + Abort
c.AbortWithStatusJSON(401, gin.H{"error": "unauthorized"})
return // 记得 return,Abort 不会自动跳出函数
}
userID := parseToken(token)
c.Set("userID", userID) // 传给后续 handler
c.Next()
}
}
// handler 中取值
func profile(c *gin.Context) {
uid, _ := c.Get("userID")
c.JSON(200, gin.H{"uid": uid})
}
Abort 之后必须 return
c.Abort() 只是设置标志阻止后续 handler,不会中断当前函数体。忘了 return 会继续往下执行,可能重复写响应导致 "headers already written" 报错。
5.5 内置 Logger 与 Recovery
r := gin.New()
r.Use(gin.Logger()) // 打印 method/path/status/latency
r.Use(gin.Recovery()) // recover panic,返回 500,防止进程崩溃
// 自定义 Recovery 处理
r.Use(gin.CustomRecovery(func(c *gin.Context, err any) {
log.Printf("panic recovered: %v", err)
c.AbortWithStatusJSON(500, gin.H{"error": "internal error"})
}))
5.6 三种挂载级别
// ① 全局:所有路由
r.Use(Timing(), CORS())
// ② 分组:仅该组
api := r.Group("/api", AuthRequired())
api.Use(RateLimit())
// ③ 单路由:仅这一条
r.GET("/admin", AuthRequired(), AdminOnly(), adminHandler)
5.7 常见自定义中间件:RequestID
func RequestID() gin.HandlerFunc {
return func(c *gin.Context) {
id := c.GetHeader("X-Request-ID")
if id == "" {
id = uuid.NewString()
}
c.Set("requestID", id)
c.Header("X-Request-ID", id) // 回写响应头,便于链路追踪
c.Next()
}
}
中间件工厂模式
约定俗成写成「返回 HandlerFunc 的函数」,好处是可以传参配置(如 RateLimit(100)),并在闭包里持有状态(如令牌桶、限流器实例)。
5.8 顺序很关键
中间件顺序决定行为,典型顺序:
- Recovery——最外层,兜住一切 panic。
- Logger / RequestID——记录每条请求。
- CORS——跨域头要尽早设置。
- RateLimit——限流应在鉴权前,先挡流量。
- Auth——鉴权,失败即 Abort。
- 业务 handler——最内层。
5.9 小结
- 中间件 = 带
c.Next()分界的 HandlerFunc,前置/后置逻辑各半,构成洋葱模型。 c.Abort()阻断后续但不跳出函数,务必配return。- 用
c.Set/c.Get在链中传值;用工厂模式给中间件传参。 - 挂载分全局/分组/单路由三级,顺序影响执行行为。
- 下一章深入 参数校验与错误处理——统一错误中间件与业务错误码。