全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-18 8 分钟 ✍️ juanwangdev

中间件嵌套与执行顺序

理解中间件嵌套和执行顺序是掌握 Gin 请求处理的关键。

洋葱模型

Gin 中间件采用洋葱模型,请求从外向内穿透,响应从内向外返回:

Go
请求 → M1(前) → M2(前) → M3(前) → Handler → M3(后) → M2(后) → M1(后) → 响应

基本执行顺序

Go
func middleware1() gin.HandlerFunc {
    return func(c *gin.Context) {
        fmt.Println("M1 前")
        c.Next()
        fmt.Println("M1 后")
    }
}

func middleware2() gin.HandlerFunc {
    return func(c *gin.Context) {
        fmt.Println("M2 前")
        c.Next()
        fmt.Println("M2 后")
    }
}

func middleware3() gin.HandlerFunc {
    return func(c *gin.Context) {
        fmt.Println("M3 前")
        c.Next()
        fmt.Println("M3 后")
    }
}

func main() {
    r := gin.New()
    r.Use(middleware1())
    r.Use(middleware2())
    r.Use(middleware3())

    r.GET("/test", func(c *gin.Context) {
        fmt.Println("Handler")
        c.String(200, "ok")
    })
}

// 输出顺序:
// M1 前 → M2 前 → M3 前 → Handler → M3 后 → M2 后 → M1 后

路由组嵌套执行顺序

Go
func main() {
    r := gin.New()

    // 全局
    r.Use(globalMiddleware())

    // 第一层路由组
    v1 := r.Group("/v1")
    v1.Use(v1Middleware())

    // 第二层嵌套
    api := v1.Group("/api")
    api.Use(apiMiddleware())

    // 第三层嵌套
    admin := api.Group("/admin")
    admin.Use(adminMiddleware())

    admin.GET("/users", handler)

    // 执行顺序:
    // global(前) → v1(前) → api(前) → admin(前) → handler → admin(后) → api(后) → v1(后) → global(后)
}

混合注册顺序

Go
func main() {
    r := gin.New()

    r.Use(m1())
    r.Use(m2())

    group := r.Group("/api")
    group.Use(m3())

    r.GET("/public", m4(), publicHandler)  // 全局路由带中间件
    group.GET("/test", m5(), handler)       // 组内路由带中间件

    // /public 执行:m1 → m2 → m4 → publicHandler → m4 → m2 → m1
    // /api/test 执行:m1 → m2 → m3 → m5 → handler → m5 → m3 → m2 → m1
}

不调用 c.Next() 的行为

Go
func blockingMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        fmt.Println("Blocking 开始")
        // 不调用 c.Next()
        c.JSON(200, gin.H{"blocked": true})
        fmt.Println("Blocking 结束")
        // 后续中间件和处理函数不执行
    }
}

r.Use(blockingMiddleware())
r.Use(m2())
r.GET("/test", handler)

// 输出:Blocking 开始 → Blocking 结束
// m2 和 handler 不执行

Abort 后的执行顺序

Go
func authMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        fmt.Println("Auth 前")
        if !isAuthenticated(c) {
            c.AbortWithStatus(401)
            fmt.Println("Auth 终止")
            return
        }
        c.Next()
        fmt.Println("Auth 后")
    }
}

func logMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        fmt.Println("Log 前")
        c.Next()
        fmt.Println("Log 后") // 仍会执行
    }
}

r.Use(logMiddleware())
r.Use(authMiddleware())
r.GET("/test", handler)

// 认证失败时:
// Log 前 → Auth 前 → Auth 终止 → Log 后
// handler 不执行

执行顺序可视化

Go
正常流程:
┌─────────────────────────────────────────────┐
│ M1 前                                        │
│ ┌─────────────────────────────────────────┐ │
│ │ M2 前                                    │ │
│ │ ┌───────────────────────────────────┐   │ │
│ │ │ Handler                            │   │ │
│ │ └───────────────────────────────────┘   │ │
│ │ M2 后                                    │ │
│ └─────────────────────────────────────────┘ │
│ M1 后                                        │
└─────────────────────────────────────────────┘

Abort 流程:
┌─────────────────────────────────────────────┐
│ M1 前                                        │
│ ┌─────────────────────────────────────────┐ │
│ │ M2 前 → Abort                           │ │
│ └─────────────────────────────────────────┘ │
│ M1 后 ← 继续执行                            │
└─────────────────────────────────────────────┘

执行顺序控制技巧

条件跳过后续

Go
func conditionalMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        if shouldSkip(c) {
            c.Next() // 直接跳过当前中间件的后置处理
            return
        }

        // 正常流程
        c.Next()
        // 后置处理
    }
}

手动控制跳转

text
func manualControlMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        fmt.Println("开始")

        // 条件性调用后续
        if shouldProceed(c) {
            c.Next()
        }

        fmt.Println("结束")
    }
}

实际应用场景

text
func main() {
    r := gin.New()

    // 日志(最外层)
    r.Use(requestLogger())

    // 异常恢复
    r.Use(recovery())

    // CORS
    r.Use(cors())

    // 认证 API
    auth := r.Group("/auth")
    auth.Use(authMiddleware())
    auth.Use(permissionMiddleware())
    {
        auth.GET("/users", handler)
    }

    // 公开 API(不经过认证)
    public := r.Group("/public")
    {
        public.GET("/info", handler)
    }

    // 执行顺序:
    // 公开:logger → recovery → cors → handler → cors → recovery → logger
    // 认证:logger → recovery → cors → auth → permission → handler → permission → auth → cors → recovery → logger
}

Abort 后外层中间件的后置代码仍会执行,可用于记录失败请求日志。

要点总结

  • 中间件按注册顺序从外向内执行,形成洋葱结构
  • c.Next() 调用后续处理,完成后返回当前函数
  • 不调用 c.Next() 时后续中间件和处理函数不执行
  • c.Abort() 阻止后续执行,外层后置代码仍执行
  • 路由组嵌套增加中间件层级,按层级顺序执行
  • 合理设计中间件顺序,日志放最外层,认证放业务前

📝 发现内容有误?点击此处直接编辑

← 上一篇 中间件参数传递与上下文
下一篇 → 中间件性能优化与注意事项
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库