全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-18 6 分钟 ✍️ juanwangdev

中间件参数传递与上下文

gin.Context 是请求上下文,用于在中间件和处理函数间传递数据。

c.Set 和 c.Get

基本使用

Go
func authMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 存储数据
        c.Set("user_id", 123)
        c.Set("role", "admin")
        c.Next()
    }
}

func handler(c *gin.Context) {
    // 获取数据
    userID, exists := c.Get("user_id")
    if !exists {
        c.String(500, "未找到用户ID")
        return
    }

    role, _ := c.Get("role")
    c.JSON(200, gin.H{
        "user_id": userID,
        "role":    role,
    })
}

类型安全获取

Go
func getInt(c *gin.Context, key string) (int, bool) {
    val, exists := c.Get(key)
    if !exists {
        return 0, false
    }

    intVal, ok := val.(int)
    return intVal, ok
}

func getString(c *gin.Context, key string) (string, bool) {
    val, exists := c.Get(key)
    if !exists {
        return "", false
    }

    strVal, ok := val.(string)
    return strVal, ok
}

// 使用
func handler(c *gin.Context) {
    userID, ok := getInt(c, "user_id")
    if !ok {
        c.String(500, "user_id 类型错误")
        return
    }
    c.JSON(200, gin.H{"user_id": userID})
}

结构体传递

Go
type RequestContext struct {
    UserID    int
    Username  string
    Role      string
    RequestID string
}

func contextMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        ctx := &RequestContext{
            UserID:    123,
            Username:  "admin",
            Role:      "admin",
            RequestID: generateRequestID(),
        }
        c.Set("ctx", ctx)
        c.Next()
    }
}

func handler(c *gin.Context) {
    ctxVal, exists := c.Get("ctx")
    if !exists {
        c.String(500, "上下文不存在")
        return
    }

    ctx, ok := ctxVal.(*RequestContext)
    if !ok {
        c.String(500, "上下文类型错误")
        return
    }

    c.JSON(200, gin.H{
        "user_id":   ctx.UserID,
        "username":  ctx.Username,
        "request_id": ctx.RequestID,
    })
}

数据流向

中间件按注册顺序传递数据:

Go
func main() {
    r := gin.New()

    r.Use(setUserIDMiddleware())   // 设置 user_id
    r.Use(setRoleMiddleware())     // 设置 role
    r.Use(checkPermissionMiddleware()) // 使用 user_id 和 role

    r.GET("/profile", handler)     // 使用所有数据
}

// 顺序:setUserID → setRole → checkPermission → handler

MustGet 方法

当确定数据存在时使用:

Go
func handler(c *gin.Context) {
    // 确定存在,不存在会 panic
    userID := c.MustGet("user_id").(int)
    role := c.MustGet("role").(string)

    c.JSON(200, gin.H{
        "user_id": userID,
        "role":    role,
    })
}

获取所有数据

Go
func debugMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Next()

        // 获取所有上下文数据
        keys := c.Keys
        for key, val := range keys {
            log.Printf("Key: %v, Value: %v", key, val)
        }
    }
}

添加请求头

Go
func headerMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        userID, _ := c.Get("user_id")
        c.Header("X-User-ID", fmt.Sprintf("%d", userID))
        c.Next()
    }
}

数据修改

中间件可以修改上下文数据:

Go
func modifyMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Set("step", 1)
        c.Next()

        // 后置修改
        step, _ := c.Get("step")
        c.Set("step", step.(int)+1)
    }
}

避免的问题

Go
// ❌ 错误:不要直接修改 gin.Context
func badMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 不要保存 c 到全局变量
        globalContext = c // 危险!
        c.Next()
    }
}

// ✅ 正确:只存储数据
func goodMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        userID := extractUserID(c)
        c.Set("user_id", userID) // 安全
        c.Next()
    }
}

gin.Context 是请求级别的,每个请求独立的,不要跨请求保存。

要点总结

  • c.Set(key, value) 存储数据到上下文
  • c.Get(key) 获取数据,返回 (value, exists)
  • c.MustGet(key) 确定存在时使用,不存在会 panic
  • 使用结构体组织多个数据,便于类型安全获取
  • Context 不要跨请求传递,存在并发安全问题

📝 发现内容有误?点击此处直接编辑

← 上一篇 中间件中的c.Next()与c.Abort()
下一篇 → 中间件嵌套与执行顺序
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库