设置与获取值

在处理一个请求的过程中,经常需要在中间件和处理器之间传递数据。Gin 的 Context 提供了 SetGet 方法来实现这个功能。

基本用法

设置值

func middleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Set("userID", 123)
        c.Set("role", "admin")
        c.Next()
    }
}

获取值

func handler(c *gin.Context) {
    // 获取值(返回 interface{} 和 bool)
    userID, exists := c.Get("userID")
    if exists {
        fmt.Println(userID.(int))
    }
    
    // 使用类型特定的方法
    role := c.GetString("role")
    count := c.GetInt("count")
}

类型特定的方法

Context 提供了多种类型的获取方法:

// 获取并断言类型
c.Get(key)           // (interface{}, bool)
c.GetString(key)     // string
c.GetInt(key)        // int
c.GetInt64(key)      // int64
c.GetFloat64(key)    // float64
c.GetBool(key)       // bool
c.GetStringMap(key)  // map[string]interface{}
c.GetStringSlice(key) // []string
c.GetTime(key)       // time.Time
c.GetDuration(key)   // time.Duration

如果值不存在或类型不匹配,这些方法会返回零值。

实际应用示例

认证中间件传递用户信息

func authMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.GetHeader("Authorization")
        
        claims, err := validateToken(token)
        if err != nil {
            c.AbortWithStatusJSON(401, gin.H{"error": "unauthorized"})
            return
        }
        
        // 存储用户信息供后续使用
        c.Set("userID", claims.UserID)
        c.Set("username", claims.Username)
        c.Set("role", claims.Role)
        
        c.Next()
    }
}

func getUserHandler(c *gin.Context) {
    userID := c.GetInt("userID")
    username := c.GetString("username")
    
    c.JSON(200, gin.H{
        "user_id":  userID,
        "username": username,
    })
}

请求追踪

func requestIDMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        requestID := uuid.New().String()
        c.Set("requestID", requestID)
        
        // 也可以设置到响应头
        c.Header("X-Request-ID", requestID)
        
        c.Next()
    }
}

func logMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        c.Next()
        
        requestID := c.GetString("requestID")
        log.Printf("[%s] %s %s %d %v",
            requestID,
            c.Request.Method,
            c.Request.URL.Path,
            c.Writer.Status(),
            time.Since(start),
        )
    }
}

MustGet 方法

如果确定值一定存在,可以使用 MustGet,不存在时会 panic:

func handler(c *gin.Context) {
    // 如果 userID 不存在,会 panic
    userID := c.MustGet("userID").(int)
    fmt.Println(userID)
}

建议配合 recovery 中间件使用,或者只在确定值存在的情况下使用。

存储复杂数据

可以存储任意类型的数据:

type User struct {
    ID       int
    Username string
    Email    string
}

func middleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        user := &User{
            ID:       1,
            Username: "alice",
            Email:    "alice@example.com",
        }
        c.Set("user", user)
        c.Next()
    }
}

func handler(c *gin.Context) {
    userInterface, exists := c.Get("user")
    if !exists {
        c.JSON(401, gin.H{"error": "user not found"})
        return
    }
    
    user, ok := userInterface.(*User)
    if !ok {
        c.JSON(500, gin.H{"error": "invalid user type"})
        return
    }
    
    c.JSON(200, user)
}

获取所有值

func handler(c *gin.Context) {
    // 获取所有存储的值
    allKeys := c.Keys
    
    for key, value := range allKeys {
        fmt.Printf("%s: %v\n", key, value)
    }
}

小结

Context 的值存储功能是中间件和处理器之间传递数据的核心机制:

  • 使用 Set 存储值
  • 使用 Get 或类型特定的方法获取值
  • 常用于传递用户信息、请求 ID、权限等
  • 注意类型断言的安全性
  • 值只在当前请求内有效

这个功能让 Gin 的中间件模式变得非常灵活,可以在请求处理链的任何位置存取数据。