全局中间件

全局中间件会对所有请求生效,适合处理通用逻辑,比如日志、错误恢复、跨域等。

注册全局中间件

使用 r.Use() 注册全局中间件:

package main

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

func main() {
    r := gin.New()
    
    r.Use(gin.Logger())
    r.Use(gin.Recovery())
    
    r.Use(func(c *gin.Context) {
        start := time.Now()
        c.Next()
        log.Printf("请求耗时: %v", time.Since(start))
    })
    
    r.GET("/api", func(c *gin.Context) {
        c.JSON(200, gin.H{"message": "Hello"})
    })
    
    r.Run(":8080")
}

常用全局中间件示例

请求 ID 中间件

为每个请求生成唯一 ID,方便追踪:

import "github.com/google/uuid"

func RequestID() gin.HandlerFunc {
    return func(c *gin.Context) {
        requestID := c.GetHeader("X-Request-ID")
        
        if requestID == "" {
            requestID = uuid.New().String()
        }
        
        c.Set("RequestID", requestID)
        c.Header("X-Request-ID", requestID)
        
        c.Next()
    }
}

r.Use(RequestID())

请求耗时中间件

记录每个请求的处理时间:

func Timing() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        
        c.Next()
        
        duration := time.Since(start)
        c.Header("X-Response-Time", duration.String())
        
        log.Printf("[%s] %s %s - %d - %v",
            c.Request.Method,
            c.Request.URL.Path,
            c.ClientIP(),
            c.Writer.Status(),
            duration,
        )
    }
}

r.Use(Timing())

安全头中间件

添加常用的安全响应头:

func SecurityHeaders() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Header("X-Content-Type-Options", "nosniff")
        c.Header("X-Frame-Options", "DENY")
        c.Header("X-XSS-Protection", "1; mode=block")
        c.Header("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
        c.Next()
    }
}

r.Use(SecurityHeaders())

请求体大小限制

防止大请求攻击:

func MaxBodySize(max int64) gin.HandlerFunc {
    return func(c *gin.Context) {
        if c.Request.ContentLength > max {
            c.AbortWithStatusJSON(413, gin.H{
                "error": "请求体过大",
            })
            return
        }
        
        c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, max)
        c.Next()
    }
}

r.Use(MaxBodySize(10 * 1024 * 1024))

请求日志详细版

更完整的日志记录:

func DetailedLogger() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        path := c.Request.URL.Path
        query := c.Request.URL.RawQuery
        
        c.Next()
        
        latency := time.Since(start)
        status := c.Writer.Status()
        clientIP := c.ClientIP()
        method := c.Request.Method
        
        log.Printf("[GIN] %v | %3d | %13v | %15s | %-7s %s",
            start.Format("2006/01/02 - 15:04:05"),
            status,
            latency,
            clientIP,
            method,
            path+"?"+query,
        )
    }
}

r.Use(DetailedLogger())

条件性全局中间件

某些中间件可能只在特定条件下生效:

func ConditionalMiddleware(condition func(*gin.Context) bool, middleware gin.HandlerFunc) gin.HandlerFunc {
    return func(c *gin.Context) {
        if condition(c) {
            middleware(c)
        } else {
            c.Next()
        }
    }
}

r.Use(ConditionalMiddleware(
    func(c *gin.Context) bool {
        return !strings.HasPrefix(c.Request.URL.Path, "/health")
    },
    DetailedLogger(),
))

中间件顺序很重要

全局中间件的注册顺序决定了执行顺序:

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

执行顺序:1前 → 2前 → 3前 → 处理器 → 3后 → 2后 → 1后

一般建议顺序:

  1. Recovery(错误恢复)
  2. Logger(日志记录)
  3. RequestID(请求 ID)
  4. CORS(跨域处理)
  5. 其他业务中间件

小结

全局中间件适合处理所有请求共有的逻辑。注册时要注意顺序,先注册的中间件先执行"前"逻辑,后执行"后"逻辑。合理使用全局中间件可以让代码更加整洁,避免在每个处理器中重复相同的逻辑。