路由组中间件

不是所有中间件都需要全局生效,路由组中间件让你可以精确控制中间件的作用范围。

基本用法

路由组使用 Group() 创建,可以给整个组添加中间件:

package main

import (
    "net/http"
    "github.com/gin-gonic/gin"
)

func Auth() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.GetHeader("Authorization")
        if token == "" {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
                "error": "请先登录",
            })
            return
        }
        c.Next()
    }
}

func main() {
    r := gin.Default()
    
    api := r.Group("/api")
    api.Use(Auth())
    {
        api.GET("/profile", func(c *gin.Context) {
            c.JSON(http.StatusOK, gin.H{"user": "张三"})
        })
        
        api.GET("/settings", func(c *gin.Context) {
            c.JSON(http.StatusOK, gin.H{"theme": "dark"})
        })
    }
    
    r.GET("/public", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"message": "公开接口"})
    })
    
    r.Run(":8080")
}

/api/profile/api/settings 都需要认证,/public 不需要。

多个路由组

不同路由组可以有不同的中间件:

func AdminAuth() gin.HandlerFunc {
    return func(c *gin.Context) {
        role := c.GetHeader("X-Role")
        if role != "admin" {
            c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
                "error": "需要管理员权限",
            })
            return
        }
        c.Next()
    }
}

func UserAuth() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.GetHeader("Authorization")
        if token == "" {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
                "error": "请先登录",
            })
            return
        }
        c.Next()
    }
}

func main() {
    r := gin.Default()
    
    admin := r.Group("/admin")
    admin.Use(AdminAuth())
    {
        admin.GET("/users", listUsers)
        admin.DELETE("/users/:id", deleteUser)
    }
    
    user := r.Group("/user")
    user.Use(UserAuth())
    {
        user.GET("/profile", getProfile)
        user.PUT("/profile", updateProfile)
    }
    
    public := r.Group("/public")
    {
        public.GET("/info", getPublicInfo)
    }
    
    r.Run(":8080")
}

嵌套路由组

路由组可以嵌套,中间件会叠加:

func main() {
    r := gin.Default()
    
    api := r.Group("/api")
    api.Use(Logger())
    {
        v1 := api.Group("/v1")
        v1.Use(Auth())
        {
            v1.GET("/posts", listPosts)
            v1.POST("/posts", createPost)
            
            admin := v1.Group("/admin")
            admin.Use(AdminAuth())
            {
                admin.GET("/stats", getStats)
            }
        }
        
        v2 := api.Group("/v2")
        v2.Use(Auth(), RateLimit())
        {
            v2.GET("/posts", listPostsV2)
        }
    }
    
    r.Run(":8080")
}

/api/v1/admin/stats 会经过 Logger → Auth → AdminAuth 三个中间件。

路由组中间件传参

中间件可以通过 c.Set() 传递数据给处理器:

func ParseUser() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.GetHeader("Authorization")
        
        user, err := parseToken(token)
        if err != nil {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
                "error": "无效的令牌",
            })
            return
        }
        
        c.Set("currentUser", user)
        c.Next()
    }
}

api := r.Group("/api")
api.Use(ParseUser())
{
    api.GET("/me", func(c *gin.Context) {
        user, exists := c.Get("currentUser")
        if !exists {
            c.JSON(http.StatusInternalServerError, gin.H{"error": "用户信息丢失"})
            return
        }
        
        c.JSON(http.StatusOK, user)
    })
}

动态路由组

根据条件动态创建路由组:

func setupRoutes(r *gin.Engine, config *Config) {
    api := r.Group("/api")
    
    if config.EnableAuth {
        api.Use(Auth())
    }
    
    if config.EnableRateLimit {
        api.Use(RateLimit())
    }
    
    api.GET("/data", getData)
}

路由组中间件列表

func ListMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Next()
    }
}

func CreateMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Next()
    }
}

api := r.Group("/api")
api.Use(ListMiddleware())
{
    api.GET("/items", listItems)
    
    create := api.Group("/")
    create.Use(CreateMiddleware())
    {
        create.POST("/items", createItem)
    }
}

小结

路由组中间件让你可以精确控制中间件的作用范围,避免全局中间件带来的性能开销。嵌套路由组时,中间件会按层级叠加。合理组织路由组和中间件,可以让代码结构更清晰。