单路由中间件

有时候只需要给某个特定路由添加中间件,而不是整个路由组。Gin 支持在单个路由上直接应用中间件。

基本用法

在路由定义时直接传入中间件:

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()
    
    r.GET("/public", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"message": "公开接口"})
    })
    
    r.GET("/profile", Auth(), func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"user": "张三"})
    })
    
    r.POST("/settings", Auth(), func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"message": "设置已更新"})
    })
    
    r.Run(":8080")
}

多个中间件

单个路由可以应用多个中间件:

func Auth() gin.HandlerFunc {
    return func(c *gin.Context) {
        fmt.Println("认证中间件")
        c.Next()
    }
}

func RateLimit() gin.HandlerFunc {
    return func(c *gin.Context) {
        fmt.Println("限流中间件")
        c.Next()
    }
}

func Audit() gin.HandlerFunc {
    return func(c *gin.Context) {
        fmt.Println("审计中间件")
        c.Next()
    }
}

r.POST("/sensitive", Auth(), RateLimit(), Audit(), func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{"message": "敏感操作完成"})
})

执行顺序:Auth → RateLimit → Audit → 处理器

实际应用示例

需要认证的接口

r.GET("/user/profile", Auth(), getUserProfile)
r.PUT("/user/profile", Auth(), updateUserProfile)
r.DELETE("/user/account", Auth(), deleteAccount)

需要管理员权限的接口

func AdminOnly() gin.HandlerFunc {
    return func(c *gin.Context) {
        role, _ := c.Get("userRole")
        if role != "admin" {
            c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
                "error": "需要管理员权限",
            })
            return
        }
        c.Next()
    }
}

r.DELETE("/users/:id", Auth(), AdminOnly(), deleteUser)
r.GET("/admin/stats", Auth(), AdminOnly(), getStats)

需要特殊验证的接口

func ValidatePost() gin.HandlerFunc {
    return func(c *gin.Context) {
        var post struct {
            Title   string `json:"title" binding:"required,min=3"`
            Content string `json:"content" binding:"required,min=10"`
        }
        
        if err := c.ShouldBindJSON(&post); err != nil {
            c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
                "error": err.Error(),
            })
            return
        }
        
        c.Set("post", post)
        c.Next()
    }
}

r.POST("/posts", Auth(), ValidatePost(), createPost)

文件上传验证

func ValidateUpload(maxSize int64, allowedTypes []string) gin.HandlerFunc {
    return func(c *gin.Context) {
        file, err := c.FormFile("file")
        if err != nil {
            c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
                "error": "请选择文件",
            })
            return
        }
        
        if file.Size > maxSize {
            c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
                "error": "文件过大",
            })
            return
        }
        
        c.Set("uploadedFile", file)
        c.Next()
    }
}

r.POST("/upload", Auth(), ValidateUpload(5*1024*1024, []string{".jpg", ".png"}), handleUpload)

与路由组中间件对比

单路由中间件和路由组中间件可以结合使用:

api := r.Group("/api")
api.Use(Auth())
{
    api.GET("/posts", listPosts)
    api.POST("/posts", createPost)
    
    api.DELETE("/posts/:id", AdminOnly(), deletePost)
}

/api/posts 只需要 Auth,/api/posts/:id 的删除需要 Auth + AdminOnly。

动态选择中间件

根据条件选择中间件:

func withOptionalAuth() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.GetHeader("Authorization")
        if token != "" {
            user, err := validateToken(token)
            if err == nil {
                c.Set("user", user)
            }
        }
        c.Next()
    }
}

r.GET("/posts", withOptionalAuth(), listPosts)

小结

单路由中间件适合只需要在特定接口上应用的逻辑。使用方式很简单,在路由定义时传入中间件函数即可。多个中间件按传入顺序执行。合理组合单路由中间件和路由组中间件,可以灵活控制每个接口的访问策略。