Context 提供了键值存储功能,用于在中间件和处理器之间传递数据。
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.Use(func(c *gin.Context) {
c.Set("appName", "MyApp")
c.Set("version", "1.0.0")
c.Next()
})
r.GET("/info", func(c *gin.Context) {
appName, exists := c.Get("appName")
if !exists {
c.String(http.StatusInternalServerError, "应用名称未设置")
return
}
version, _ := c.Get("version")
c.JSON(http.StatusOK, gin.H{
"app": appName,
"version": version,
})
})
r.Run(":8080")
}
当确定值存在时,可以用 MustGet:
r.GET("/must", func(c *gin.Context) {
appName := c.MustGet("appName").(string)
c.String(http.StatusOK, "应用: %s", appName)
})
如果值不存在,MustGet 会 panic,所以只在确定值存在时使用。
Gin 提供了类型安全的获取方法:
r.GET("/typed", func(c *gin.Context) {
c.Set("name", "张三")
c.Set("age", 25)
c.Set("score", 95.5)
c.Set("active", true)
c.Set("createdAt", time.Now())
c.Set("timeout", time.Hour)
name := c.GetString("name")
age := c.GetInt("age")
score := c.GetFloat("score")
active := c.GetBool("active")
createdAt := c.GetTime("createdAt")
timeout := c.GetDuration("timeout")
c.JSON(http.StatusOK, gin.H{
"name": name,
"age": age,
"score": score,
"active": active,
"createdAt": createdAt,
"timeout": timeout,
})
})
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Role string `json:"role"`
}
func Auth() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
user := &User{
ID: 1,
Name: "张三",
Email: "zhangsan@example.com",
Role: "admin",
}
c.Set("currentUser", user)
c.Set("userID", user.ID)
c.Set("userRole", user.Role)
c.Next()
}
}
r.GET("/profile", Auth(), func(c *gin.Context) {
user, exists := c.Get("currentUser")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{"error": "未登录"})
return
}
u := user.(*User)
c.JSON(http.StatusOK, u)
})
封装便捷的获取函数:
func GetCurrentUser(c *gin.Context) (*User, error) {
val, exists := c.Get("currentUser")
if !exists {
return nil, errors.New("用户未登录")
}
user, ok := val.(*User)
if !ok {
return nil, errors.New("用户信息格式错误")
}
return user, nil
}
func GetUserID(c *gin.Context) (int, error) {
val, exists := c.Get("userID")
if !exists {
return 0, errors.New("用户ID不存在")
}
id, ok := val.(int)
if !ok {
return 0, errors.New("用户ID格式错误")
}
return id, nil
}
func IsAdmin(c *gin.Context) bool {
role, exists := c.Get("userRole")
if !exists {
return false
}
return role == "admin"
}
r.GET("/me", Auth(), func(c *gin.Context) {
user, err := GetCurrentUser(c)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, user)
})
func SetRequestContext(c *gin.Context, data map[string]interface{}) {
for k, v := range data {
c.Set(k, v)
}
}
r.Use(func(c *gin.Context) {
SetRequestContext(c, map[string]interface{}{
"requestID": uuid.New().String(),
"startTime": time.Now(),
"clientIP": c.ClientIP(),
})
c.Next()
})
r.GET("/check", func(c *gin.Context) {
if _, exists := c.Get("currentUser"); exists {
c.String(http.StatusOK, "已登录")
} else {
c.String(http.StatusOK, "未登录")
}
})
r.GET("/all", func(c *gin.Context) {
c.Set("key1", "value1")
c.Set("key2", "value2")
keys := c.Keys
c.JSON(http.StatusOK, keys)
})
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()
}
}
func Timer() gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("startTime", time.Now())
c.Next()
start, _ := c.Get("startTime")
duration := time.Since(start.(time.Time))
requestID, _ := c.Get("requestID")
log.Printf("[%s] %s %s - %v",
requestID,
c.Request.Method,
c.Request.URL.Path,
duration,
)
}
}
r.Use(RequestID())
r.Use(Timer())
Context 的数据存储功能是中间件通信的基础。使用 Set 和 Get 存取数据,MustGet 用于确定存在的值。建议封装便捷的获取函数,处理类型断言和错误情况。记住 Context 的数据只在当前请求有效。