中间件原理:Use、Next/Abort 与执行顺序

本章解决什么问题:中间件是 Gin 扩展能力的核心机制。只有真正理解 c.Next/c.Abort 与处理器链的调用顺序,才能写出不踩坑的日志、鉴权与恢复代码。

一条请求的处理链

注册路由时,gin 会把你传入的所有处理器按顺序串成一条链:r.Use 注册的引擎级中间件在最前,之后是组级、路由级中间件,最后是真正的业务 handler。gin 逐个调用,前一个通过 c.Next() 把控制权交给下一个。

Next 与 Abort

  • c.Next():继续执行链中下一个处理器。Next 之前的代码先跑,Next 返回后再跑“善后”代码——相当于请求“去”与“回”两个阶段都归你管;
  • c.Abort():跳过链中剩余的处理器(含业务 handler)。注意它不会中止当前中间件自身,想立即返回要配合 return,或直接使用 AbortWithStatusJSON 这类“中止 + 响应”的组合方法。
package main

import (
	"fmt"

	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.New() // 裸引擎,便于观察顺序
	r.Use(first(), second())
	r.GET("/demo", func(c *gin.Context) { c.String(200, "ok") })
	r.Run(":8080")
}

func first() gin.HandlerFunc {
	return func(c *gin.Context) {
		fmt.Println("first: before")
		c.Next()
		fmt.Println("first: after")
	}
}

func second() gin.HandlerFunc {
	return func(c *gin.Context) {
		fmt.Println("second: before")
		c.Abort() // 跳过后续业务 handler
		fmt.Println("second: after abort")
	}
}

输出顺序:first:before → second:before → second:after abort → first:after,业务 handler 被 Abort 跳过,验证了“Abort 只拦后面的链,不拦自己”。

用 Context 传递数据

中间件与 handler 之间共享数据用 c.Set(key, value) 写入、c.Get(key) 读取,取用时可配合 c.GetString、c.GetInt 等类型化方法:

func requestID() gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Set("req_id", "abc-123") // 上游写入
		c.Next()
	}
}

在 handler 里用 id := c.GetString("req_id") 取回。Set/Get 只在同一次请求的链条内有效,跨请求不共享。

关键点

  • 链顺序:引擎级 → 组级 → 路由级 → 业务 handler;Next 之前是前置,Next 之后是后置。
  • Abort 只跳过后续处理器;需要中断自己时必须 return。
  • gin.New() 不带默认中间件,gin.Default() 就是 New + Logger + Recovery。
  • 中间件里的 panic 若无恢复中间件兜底会拖垮进程,生产务必挂 Recovery(第 12、13 章)。

小结

“洋葱模型”式的执行链是 Gin 中间件的灵魂:Next 控制深入,Abort 控制截断,Set/Get 负责同请求内传话。下一章看 Gin 自带的两个中间件。

笔记加载中…