中间件基础

中间件是 Gin 框架的精髓之一,理解它的工作原理对写出优雅的代码至关重要。

什么是中间件

中间件是位于请求和处理器之间的函数,可以在请求到达处理器之前或之后执行代码。典型的用途包括:

  • 日志记录
  • 权限验证
  • 请求限流
  • 错误恢复
  • 数据压缩

中间件的基本结构

一个中间件就是一个返回 gin.HandlerFunc 的函数:

package main

import (
    "fmt"
    "time"
    
    "github.com/gin-gonic/gin"
)

func Logger() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        
        c.Next()
        
        duration := time.Since(start)
        fmt.Printf("[%s] %s %s - %v\n",
            time.Now().Format("2006-01-02 15:04:05"),
            c.Request.Method,
            c.Request.URL.Path,
            duration,
        )
    }
}

func main() {
    r := gin.New()
    
    r.Use(Logger())
    
    r.GET("/hello", func(c *gin.Context) {
        c.String(200, "Hello, World!")
    })
    
    r.Run(":8080")
}

Next() 的作用

c.Next() 是中间件的核心,它会把请求传递给下一个中间件或处理器:

func Middleware1() gin.HandlerFunc {
    return func(c *gin.Context) {
        fmt.Println("Middleware1 - 请求前")
        
        c.Next()
        
        fmt.Println("Middleware1 - 请求后")
    }
}

func Middleware2() gin.HandlerFunc {
    return func(c *gin.Context) {
        fmt.Println("Middleware2 - 请求前")
        
        c.Next()
        
        fmt.Println("Middleware2 - 请求后")
    }
}

r.Use(Middleware1())
r.Use(Middleware2())

r.GET("/test", func(c *gin.Context) {
    fmt.Println("处理器执行")
    c.String(200, "OK")
})

访问 /test 的输出顺序:

Middleware1 - 请求前
Middleware2 - 请求前
处理器执行
Middleware2 - 请求后
Middleware1 - 请求后

这就是所谓的"洋葱模型":请求从外层进入,响应从内层返回。

Abort() 中断请求

有时候需要在中间件里中断请求,比如权限验证失败:

func Auth() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.GetHeader("Authorization")
        
        if token == "" {
            c.JSON(401, gin.H{"error": "未授权"})
            c.Abort()
            return
        }
        
        if !validateToken(token) {
            c.JSON(403, gin.H{"error": "无效的令牌"})
            c.Abort()
            return
        }
        
        c.Next()
    }
}

func validateToken(token string) bool {
    return token == "valid-token"
}

r.Use(Auth())

r.GET("/protected", func(c *gin.Context) {
    c.JSON(200, gin.H{"message": "访问成功"})
})

调用 c.Abort() 后,后续的中间件和处理器都不会执行。

AbortWithStatus

Gin 提供了更简洁的中断方法:

func Auth() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.GetHeader("Authorization")
        
        if token == "" {
            c.AbortWithStatusJSON(401, gin.H{"error": "未授权"})
            return
        }
        
        c.Next()
    }
}

常用的中断方法:

c.Abort()
c.AbortWithStatus(401)
c.AbortWithStatusJSON(401, gin.H{"error": "未授权"})

中间件执行顺序

中间件按照注册顺序执行:

r.Use(MiddlewareA())
r.Use(MiddlewareB())
r.Use(MiddlewareC())

请求流程:A前 → B前 → C前 → 处理器 → C后 → B后 → A后

gin.Default() 的默认中间件

gin.Default() 默认添加了 Logger 和 Recovery 中间件:

func Default() *Engine {
    engine := New()
    engine.Use(Logger(), Recovery())
    return engine
}

如果不需要这些默认中间件,使用 gin.New() 创建实例。

小结

中间件是 Gin 框架的核心特性,通过 c.Next()c.Abort() 控制请求流程。理解洋葱模型对正确使用中间件很重要。下一章我们会学习如何在不同范围内应用中间件。