Gin.Use()注册的中间件无法自动放行特定路径,因其为全局生效;需在中间件内手动判断路径(如/healthz)并调用c.Next()放行,或通过分组路由(如api.Group("/api"))隔离中间件作用域。

为什么 gin.Use() 注册的中间件无法对某些路径“放行”
因为 gin.Use() 注册的是全局中间件,它作用于所有匹配请求,包括 /healthz、/metrics、/static/* 甚至 404 路径。Gin 没有内置“排除路径”机制,所谓“跳过”只能靠中间件自己判断并主动放行,而不是引擎层面过滤。
在中间件内部做路径白名单判断(最常用)
这是实际项目中最可行、也最可控的方式。关键不是让中间件不执行,而是让它执行后立刻 c.Next() 并返回,不干扰后续流程。
- 用
strings.HasPrefix(c.Request.URL.Path, "/healthz")或正则匹配目标路径 - 匹配成功后必须调用
c.Next(),否则路由 handler 不会执行 - 不要只写
return,漏掉c.Next()会导致请求静默卡住 - 示例逻辑:
func skipForHealth(c *gin.Context) {
if c.Request.URL.Path == "/healthz" || c.Request.URL.Path == "/readyz" {
c.Next() // 放行,继续走后续 handler(比如你注册的 /healthz handler)
return
}
// 正常中间件逻辑,比如鉴权、日志、限流
if !isValidToken(c) {
c.AbortWithStatusJSON(401, gin.H{"error": "unauthorized"})
return
}
c.Next()
}
用路由分组隔离中间件(更干净)
如果大部分 API 需要中间件,但健康检查、静态资源等完全不需要,就别把它们塞进同一个 gin.Engine 实例里。
- 为业务 API 单独建分组:
api := r.Group("/api"),再api.Use(authMiddleware, rateLimitMiddleware) - 健康检查直接挂在根引擎:
r.GET("/healthz", healthHandler),它天然不受api.Use()影响 - 静态文件也同理:
r.Static("/static", "./assets"),不经过任何Use()中间件 - 注意:不要在
r.Use()后再注册r.NoRoute(),否则兜底逻辑也会被全局中间件拦截
启动时用 gin.New() 避免默认中间件污染
gin.Default() 会自动加 Logger() 和 Recovery(),这两个本身也是全局中间件。如果你只想对 /api/* 做日志,却不想给 /healthz 打日志,那默认日志就成干扰项了。
- 改用
r := gin.New(),从零开始控制 - 手动加你需要的日志中间件,且只加在业务分组上:
api.Use(gin.LoggerWithWriter(...)) - 这样
/healthz就完全不会触发任何日志写入或 panic 捕获 - 但记得补上
Recovery()—— 否则 panic 会直接导致进程退出
/api/v1/users 和 /api/v2/users 共享同一套鉴权中间件,但其中某个版本需要绕过限流。这种场景下,光靠路径前缀判断就不够了,得结合 query、header 甚至 context value 做细粒度决策,而这些都得在中间件里手工实现。


















