中间件概念 #

一、中间件概述 #

1.1 什么是中间件 #

中间件是位于请求和响应之间的处理函数,可以在请求到达处理函数之前或响应返回客户端之后执行特定逻辑:

text
请求 → 中间件1 → 中间件2 → 处理函数 → 中间件2 → 中间件1 → 响应

1.2 中间件的作用 #

作用 示例
请求预处理 解析Token、验证权限
日志记录 记录请求信息
错误恢复 捕获panic
性能监控 统计请求耗时
数据处理 解析请求体
响应处理 统一响应格式

1.3 中间件类型定义 #

go
// HandlerFunc 是Gin中间件和处理函数的类型
type HandlerFunc func(*Context)

// 中间件本质上就是一个HandlerFunc
func Middleware() HandlerFunc {
    return func(c *Context) {
        // 前置处理
        c.Next() // 调用下一个处理函数
        // 后置处理
    }
}

二、洋葱模型 #

2.1 洋葱模型示意 #

text
                    ┌──────────────────┐
                    │   中间件1 开始    │
                    │                  │
                    │  ┌────────────┐  │
                    │  │ 中间件2 开始│  │
                    │  │            │  │
                    │  │ ┌────────┐ │  │
                    │  │ │ 处理函数│ │  │
                    │  │ └────────┘ │  │
                    │  │ 中间件2 结束│  │
                    │  └────────────┘  │
                    │   中间件1 结束    │
                    └──────────────────┘

2.2 执行流程演示 #

go
func main() {
    r := gin.New()
    
    r.Use(
        func(c *gin.Context) {
            fmt.Println("1 - 中间件开始")
            c.Next()
            fmt.Println("1 - 中间件结束")
        },
        func(c *gin.Context) {
            fmt.Println("2 - 中间件开始")
            c.Next()
            fmt.Println("2 - 中间件结束")
        },
        func(c *gin.Context) {
            fmt.Println("3 - 中间件开始")
            c.Next()
            fmt.Println("3 - 中间件结束")
        },
    )
    
    r.GET("/", func(c *gin.Context) {
        fmt.Println("处理函数")
        c.String(200, "ok")
    })
    
    r.Run()
}

// 输出顺序:
// 1 - 中间件开始
// 2 - 中间件开始
// 3 - 中间件开始
// 处理函数
// 3 - 中间件结束
// 2 - 中间件结束
// 1 - 中间件结束

2.3 c.Next()的作用 #

go
func Middleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 前置处理:在c.Next()之前执行
        fmt.Println("Before Next")
        
        // c.Next()调用下一个处理函数
        c.Next()
        
        // 后置处理:在c.Next()之后执行
        fmt.Println("After Next")
    }
}

三、Context核心方法 #

3.1 Next方法 #

go
// Next执行链中的下一个处理函数
func (c *Context) Next() {
    c.index++
    for c.index < int8(len(c.handlers)) {
        c.handlers[c.index](c)
        c.index++
    }
}

3.2 Abort方法 #

go
func main() {
    r := gin.New()
    
    r.Use(
        func(c *gin.Context) {
            fmt.Println("中间件1 - 开始")
            c.Next()
            fmt.Println("中间件1 - 结束")
        },
        func(c *gin.Context) {
            fmt.Println("中间件2 - 开始")
            c.Abort() // 中断后续处理
            fmt.Println("中间件2 - 结束")
        },
        func(c *gin.Context) {
            fmt.Println("中间件3 - 不会执行")
            c.Next()
        },
    )
    
    r.GET("/", func(c *gin.Context) {
        fmt.Println("处理函数 - 不会执行")
        c.String(200, "ok")
    })
    
    r.Run()
}

// 输出:
// 中间件1 - 开始
// 中间件2 - 开始
// 中间件2 - 结束
// 中间件1 - 结束

3.3 AbortWithStatus #

go
func AuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.GetHeader("Authorization")
        if token == "" {
            // 中断并返回状态码
            c.AbortWithStatus(401)
            return
        }
        c.Next()
    }
}

3.4 AbortWithStatusJSON #

go
func AuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.GetHeader("Authorization")
        if token == "" {
            // 中断并返回JSON
            c.AbortWithStatusJSON(401, gin.H{
                "code":    401,
                "message": "未授权",
            })
            return
        }
        c.Next()
    }
}

3.5 IsAborted #

go
func main() {
    r := gin.New()
    
    r.Use(
        func(c *gin.Context) {
            c.Next()
            
            if c.IsAborted() {
                fmt.Println("请求被中断")
            }
        },
        func(c *gin.Context) {
            c.Abort()
        },
    )
    
    r.Run()
}

四、Context数据传递 #

4.1 Set和Get #

go
func main() {
    r := gin.New()
    
    r.Use(func(c *gin.Context) {
        // 设置上下文数据
        c.Set("userId", "123")
        c.Set("role", "admin")
        c.Next()
    })
    
    r.GET("/", func(c *gin.Context) {
        // 获取上下文数据
        userId, exists := c.Get("userId")
        if exists {
            fmt.Println("UserId:", userId)
        }
        
        role, exists := c.Get("role")
        if exists {
            fmt.Println("Role:", role)
        }
        
        c.String(200, "ok")
    })
    
    r.Run()
}

4.2 类型安全的Get方法 #

go
func main() {
    r := gin.New()
    
    r.Use(func(c *gin.Context) {
        c.Set("userId", 123)
        c.Set("name", "Alice")
        c.Set("admin", true)
        c.Next()
    })
    
    r.GET("/", func(c *gin.Context) {
        // MustGet: 如果不存在会panic
        userId := c.MustGet("userId").(int)
        
        // GetString: 获取字符串
        name := c.GetString("name")
        
        // GetInt: 获取整数
        id := c.GetInt("userId")
        
        // GetBool: 获取布尔值
        isAdmin := c.GetBool("admin")
        
        c.JSON(200, gin.H{
            "userId":  userId,
            "name":    name,
            "id":      id,
            "isAdmin": isAdmin,
        })
    })
    
    r.Run()
}

4.3 Context数据类型 #

方法 返回类型
Get(key) interface{}, bool
MustGet(key) interface{}
GetString(key) string
GetInt(key) int
GetInt64(key) int64
GetFloat64(key) float64
GetBool(key) bool
GetTime(key) time.Time
GetDuration(key) time.Duration

五、中间件注册方式 #

5.1 全局中间件 #

go
func main() {
    r := gin.New()
    
    // 全局中间件
    r.Use(gin.Logger())
    r.Use(gin.Recovery())
    
    // 或链式注册
    r.Use(
        gin.Logger(),
        gin.Recovery(),
        CustomMiddleware(),
    )
    
    r.Run()
}

5.2 分组中间件 #

go
func main() {
    r := gin.New()
    
    api := r.Group("/api")
    api.Use(AuthMiddleware())
    {
        api.GET("/users", listUsers)
        api.GET("/posts", listPosts)
    }
    
    r.Run()
}

5.3 路由中间件 #

go
func main() {
    r := gin.New()
    
    r.GET("/admin",
        AuthMiddleware(),
        AdminMiddleware(),
        adminHandler,
    )
    
    r.Run()
}

5.4 默认中间件 #

go
func main() {
    // gin.Default() 包含Logger和Recovery中间件
    r := gin.Default()
    
    // 等价于
    r := gin.New()
    r.Use(gin.Logger())
    r.Use(gin.Recovery())
    
    r.Run()
}

六、中间件执行顺序 #

6.1 注册顺序即执行顺序 #

go
func main() {
    r := gin.New()
    
    // 全局中间件
    r.Use(GlobalMiddleware1())
    r.Use(GlobalMiddleware2())
    
    api := r.Group("/api")
    // 分组中间件
    api.Use(GroupMiddleware1())
    api.Use(GroupMiddleware2())
    {
        // 路由中间件
        api.GET("/test",
            RouteMiddleware1(),
            RouteMiddleware2(),
            handler,
        )
    }
    
    r.Run()
}

// 执行顺序:
// GlobalMiddleware1 Before
// GlobalMiddleware2 Before
// GroupMiddleware1 Before
// GroupMiddleware2 Before
// RouteMiddleware1 Before
// RouteMiddleware2 Before
// handler
// RouteMiddleware2 After
// RouteMiddleware1 After
// GroupMiddleware2 After
// GroupMiddleware1 After
// GlobalMiddleware2 After
// GlobalMiddleware1 After

6.2 中间件索引 #

go
// Gin内部维护一个handlers切片和索引
type Context struct {
    handlers []HandlerFunc
    index    int8
}

// 初始index = -1
// 每次调用Next(),index++
// 当index >= len(handlers)时停止

七、中间件最佳实践 #

7.1 单一职责 #

go
// 好的做法:每个中间件只做一件事
func LoggerMiddleware() gin.HandlerFunc { ... }
func AuthMiddleware() gin.HandlerFunc { ... }
func CORSMiddleware() gin.HandlerFunc { ... }

// 不好的做法:一个中间件做多件事
func AllInOneMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 日志
        // 认证
        // CORS
        // ...
    }
}

7.2 错误处理 #

go
func ErrorHandlerMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        defer func() {
            if err := recover(); err != nil {
                log.Printf("Panic recovered: %v", err)
                c.AbortWithStatusJSON(500, gin.H{
                    "code":    500,
                    "message": "服务器内部错误",
                })
            }
        }()
        
        c.Next()
        
        if len(c.Errors) > 0 {
            c.JSON(500, gin.H{
                "code":    500,
                "message": c.Errors.String(),
            })
        }
    }
}

7.3 性能考虑 #

go
func PerformanceMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        
        c.Next()
        
        latency := time.Since(start)
        
        // 慢请求告警
        if latency > time.Second {
            log.Printf("Slow request: %s %s took %v",
                c.Request.Method,
                c.Request.URL.Path,
                latency,
            )
        }
    }
}

八、总结 #

8.1 核心要点 #

要点 说明
洋葱模型 请求穿过多层中间件
c.Next() 调用下一个处理函数
c.Abort() 中断后续处理
Context 中间件间数据传递

8.2 最佳实践 #

实践 说明
单一职责 每个中间件只做一件事
错误恢复 使用defer recover
性能监控 记录请求耗时
日志记录 记录关键信息

8.3 下一步 #

现在你已经理解了中间件概念,接下来让我们学习 全局中间件,掌握全局中间件的应用!

最后更新:2026-03-28