全局中间件必须在r.Use()后、r.Group()/r.GET()前注册,按顺序执行;路由组中间件仅作用于该组及子组;c.Next()是流程接力点,漏写会导致请求卡住;环境相关中间件需启动时静态配置。

全局中间件必须在 r.Use() 之后、任何 r.Group() 或 r.GET() 之前注册
全局中间件对所有后续注册的路由生效,包括后续创建的 Group 和其子路由。一旦在 r.Use() 后又调用了 r.Group(),这个 group 就会继承当前已注册的全局中间件链;但反过来,如果先注册 group 再调用 r.Use(),那个中间件不会注入到已存在的 group 中。
常见错误现象:r.Use(authMiddleware) 写在 r.Group("/admin") 之后,结果 admin 路由没被鉴权拦截。
- 务必把所有
r.Use(...)放在r.Group()和r.GET()等注册语句之前 - 多个全局中间件按顺序传入
r.Use(m1, m2, m3),执行顺序即声明顺序(洋葱模型外层→内层) -
gin.Default()自带Logger和Recovery,若用gin.New(),这两者需手动加
路由组中间件只作用于该 group 及其嵌套子 group,不穿透兄弟 group
路由组中间件是“局部生效”的典型场景,比如 admin := r.Group("/admin", authMiddleware),这个 authMiddleware 只影响 /admin/* 下的所有路由,和同级的 /api/v1 group 完全无关。
容易踩的坑:authorized := r.Group("/").Use(AuthRequired()) 看似“全局”,其实只是给根路径 group 加了中间件,而 r.GET("/", ...) 是直接挂在 engine 上的,不受它影响。
- 推荐写法:
admin := r.Group("/admin"); admin.Use(authMiddleware),显式清晰 - 嵌套路由组会自动继承父 group 的中间件:如果
v1 := r.Group("/api/v1")没加中间件,但users := v1.Group("/users")加了users.Use(logMiddleware),那只有/api/v1/users/*生效 - 不要在 handler 函数里调用
group.Use()—— 运行时无效,还可能 panic
ctx.Next() 是控制中间件执行流程的关键,漏掉会导致后续逻辑不执行
中间件函数里如果不调用 c.Next(),请求流程就会卡住,后续所有中间件和最终 handler 都不会运行,客户端会一直等待超时或收到空响应。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
典型误操作:写了个日志中间件,只打印开始时间,忘了加 c.Next(),结果所有接口都 hang 住。
-
c.Next()表示“把控制权交给下一个 handler”,它不是可选的,而是流程接力点 - 想做后置处理(比如统计耗时),必须把代码写在
c.Next()之后 - 用
c.Abort()可提前终止流程(如未登录直接返回 401),此时c.Next()不会被执行,后续 handler 全跳过
环境变量驱动的条件化中间件注入必须在启动阶段完成
所谓“动态开关中间件”,不能靠请求进来再判断是否启用,而应在 r.Run() 前根据 os.Getenv("ENV") 等决定是否调用 group.Use() 或 r.Use()。
例如开发环境跳过 JWT 校验,生产环境强制校验 —— 这个判断只能出现在 main 启动流程里,绝不能塞进某个 handler 中。
- 错误示范:
func handler(c *gin.Context) { if os.Getenv("SKIP_AUTH") == "true" { /* 不调用 c.Next() */ } else { c.Next() } }—— 这不是注入中间件,是业务逻辑污染 - 正确做法:启动时读取配置,只注册需要的中间件链,让路由树结构本身反映环境差异
- 注意字符串比较要严格:
os.Getenv("ENABLE_ADMIN") == "true",别用!= "",避免空格或大小写干扰
实际项目里最常被忽略的,是中间件作用域的“静态性”——它由注册时机和位置决定,跟请求路径无关。哪怕你把 authMiddleware 写得再智能,只要没在正确的 group 上调用 Use(),它就永远不会跑。


















