中间件必须注册在mvc.Application创建之后、Handle之前,正确顺序是:先app.Party()→mvc.New()→Use()/UseGlobal()→Handle();Use()作用于当前controller所有方法,UseGlobal()注入到每个handler最前端;鉴权失败需StatusCode+JSON+StopExecution()三者缺一不可。

中间件必须注册在mvc.Application创建之后、Handle之前
你不能把中间件直接塞进 app.Use() 就完事——Iris 的 MVC 层有自己的生命周期,全局中间件(app.Use)对 controller 方法的参数绑定、返回值处理等环节不生效。真正起作用的是绑定到 mvc.Application 实例上的中间件。
正确顺序是:先用 app.Party() 创建路由分组 → 传给 mvc.New() 得到 *mvc.Application → 调用其 Use() 或 UseGlobal() → 最后调用 Handle() 注册控制器。
-
Use():只作用于当前 MVC 应用注册的所有 handler(即该 controller 的所有方法) -
UseGlobal():会注入到每个 handler 执行链最前端,包括BeginRequest/EndRequest - 别在
BeforeActivation里调b.Router().Use()—— 这注册的是底层路由中间件,绕过 MVC 流程,参数绑定和错误恢复可能失效
鉴权类中间件要配合 ctx.StopExecution() 使用
常见错误是 JWT 验证失败后只写响应,却忘了中断执行流。Iris 不会自动跳过后续 handler,ctx.Next() 只是“往下传”,不是“条件跳过”。
典型错误写法:ctx.JSON(401, ...); ctx.Next() → 验证失败仍会进 GetByID 方法体。
- 验证失败时:调
ctx.StatusCode(401)+ctx.JSON(...)+ctx.StopExecution(),三者缺一不可 - 验证成功时:只调
ctx.Next(),让控制权继续往下走 - 别在中间件末尾无条件写
ctx.Next(),应该用 if/else 包裹 - 如果依赖
ctx.Values().Set("user", u)向 controller 透传数据,确保StopExecution()在设置值之后、Next()之前
BeginRequest/EndRequest 是轻量级中间件替代方案
如果你只是想做日志打点、上下文初始化或清理,不用大张旗鼓写独立中间件——BeginRequest 和 EndRequest 是 controller 级的钩子函数,天然共享 struct 字段,且自动包裹整个 handler 执行周期。
它们比中间件更轻:不参与路由匹配,不经过 ctx.Next() 调度,也不受 UseGlobal() 顺序影响。
-
BeginRequest在 controller 方法执行前调用,适合读取 token、预加载用户信息 -
EndRequest在 controller 方法返回后、响应写出前调用,适合记录耗时、清理临时资源 - 注意:这两个方法签名必须是
func(c *MyController) BeginRequest(ctx iris.Context),否则框架不识别 - 它们无法提前终止请求(没有
StopExecution),如需拦截必须搭配中间件
中间件里读取路径参数要小心零值陷阱
路径参数(如 /{id:long})在中间件中不能直接用 ctx.Params().GetInt64("id") —— 因为此时 MVC 层尚未完成参数解析,GetInt64 会返回 0, false,而不是报错。
你看到的 “ID 不存在” 往往是这个原因,不是路由没匹配上。
- 安全做法:用
ctx.Params().Get("id")先取字符串,再手动strconv.ParseInt - 或者把参数校验逻辑移到 controller 方法内,那里参数已解析完毕
- 如果必须在中间件里强依赖路径参数,建议加一层检查:
if idStr := ctx.Params().Get("id"); idStr == "" { ctx.StopExecution() } -
ctx.URLParam()只读 query string,和路径参数无关,别混用
Use() 和 UseGlobal() 的作用域差异、StopExecution() 的必要性、以及路径参数在中间件阶段未就绪的事实**——这三点不厘清,中间件就会变成“看似运行、实则失效”的幽灵逻辑。


















