全局中间件必须用app.Use()注册,不能用app.Get()或app.UseGlobal();若注册在app.Run()之后、未调用app.Use()、或未调用ctx.StopExecution()终止流程,将导致中间件失效或越权执行。

全局中间件必须用 app.Use() 注册,不是 app.Get() 或 app.UseGlobal()
很多人写完中间件函数,直接塞进路由里:app.Get("/api/user", authMiddleware, handler)——这只能保护单个路由。真要“全局”生效,得调用 app.Use()。注意:app.UseGlobal() 是旧版别名(v12 已标记弃用),文档里不提,但实际仍可用;不过官方推荐统一用 app.Use(),避免混淆。
常见错误现象:中间件函数写了,也传进去了,但日志没打、token 没校验、请求照样进 handler——大概率是注册位置错了,比如写在 app.Run() 之后,或漏掉了 app.Use() 调用本身。
-
app.Use()必须在app.Run()或app.Listen()之前调用,否则不生效 - 多个
app.Use()按注册顺序执行,先注册的先运行 - 不要在中间件里重复调用
ctx.Next(),它只负责移交控制权,不自动终止后续逻辑
中间件函数里写响应后必须调 ctx.StopExecution()
这是 Iris 和 Gin 最关键的语义差异。Iris 不会自动中断后续链路,哪怕你已经调了 ctx.JSON(401, ...) 或 ctx.StatusCode(401),只要没显式调 ctx.StopExecution(),ctx.Next() 之后的中间件和最终 handler 依然会执行。
典型场景:JWT 验证失败,你返回了 ctx.JSON(401, map[string]string{"error": "invalid token"}),但忘了加 ctx.StopExecution(),结果业务 handler 还是跑了一次,可能查了 DB、改了状态,造成越权操作。
- 验证失败 → 写响应 +
ctx.StopExecution(),然后直接 return - 不要把
ctx.Next()放在 if 分支里又在外层再写一次,容易漏掉控制流 - 调试时可在中间件开头加
fmt.Println("auth middleware hit"),确认是否真被触发
路径参数和 query 参数别混用:ctx.Params().Get() vs ctx.URLParam()
中间件里常要取 token,但有人习惯性写 ctx.URLParam("token"),结果永远拿不到——因为 token 通常在 Header(Authorization: Bearer xxx)或 Cookie 里,不是 URL 查询参数。更隐蔽的是:如果误用 ctx.Params().Get("token"),而路由没定义 {token} 这种路径段,它就返回空字符串,不报错也不提示,调试时极难发现。
正确做法是明确来源:
- Header:用
ctx.GetHeader("Authorization"),再手动切"Bearer "前缀 - Cookie:用
ctx.Cookies().Get("session_id") - Query 参数(如
/login?next=/admin):才用ctx.URLParam("next") - 路径参数(如
/user/{id}):才用ctx.Params().Get("id")
中间件中修改上下文值要用 ctx.Values().Set(),别用局部变量
比如想把解析出的用户 ID 透传给后续 handler,不能写 userID := 123 然后指望 handler 能访问到。Iris 提供了 ctx.Values() 这个键值对容器,专为跨中间件/路由传递数据设计。
示例:JWT 中间件解析出 user ID 后,应写成 ctx.Values().Set("user_id", userID);handler 里用 ctx.Values().GetInt64("user_id") 取。这个容器生命周期和当前请求绑定,安全且清晰。
- 不要用全局变量存请求级数据,goroutine 不安全
-
ctx.Values().Set()的 key 推荐用常量,避免字符串拼写错误 - 取值前最好用
ctx.Values().GetExists("user_id")判断是否存在,而不是直接强转


















