中间件参数传递与上下文
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 不要跨请求传递,存在并发安全问题
📝 发现内容有误?点击此处直接编辑