中间件概念 #
一、中间件概述 #
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