必须调用 ctx.StopExecution() 才能终止中间件链:即使已写响应,不调用它后续中间件和 handler 仍会执行,易导致越权等严重问题。

中间件里写完响应后必须调 ctx.StopExecution()
这是 Iris 最关键也最容易被忽略的语义:哪怕你已经调了 ctx.StatusCode(401)、ctx.JSON() 或 ctx.WriteString(),只要没显式调 ctx.StopExecution(),后续所有中间件和最终 handler 仍会执行。
典型错误场景是 JWT 验证失败后只返回错误响应,却漏掉 ctx.StopExecution(),结果业务逻辑照常查 DB、改状态,造成越权操作。
- 所有提前终止请求的分支(如鉴权失败、参数校验不通过)末尾都必须跟
ctx.StopExecution() -
ctx.StopExecution()不会自动触发 panic,也不会影响日志或 defer,它只是切断中间件链 - 不能靠
return代替 ——return只退出当前函数,ctx.Next()若在它之后仍会被执行
ctx.Next() 不是“继续执行”的保证,而是“移交控制权”的指令
ctx.Next() 的作用非常具体:把当前请求的控制权交给下一个中间件或最终 handler。它不检查是否已写响应,也不阻止你后续再写响应 —— 这意味着你可能在同一个请求中多次写 body,导致 http: multiple response.WriteHeader calls panic。
常见反模式:
- 在 if 错误分支里写了
ctx.JSON(400, ...),但分支外还写了ctx.Next() - 用
defer ctx.Next()—— Iris 不支持这种写法,defer 会在函数退出时才调,此时 handler 可能已执行完毕 - 把
ctx.Next()放在日志或耗时操作之前,导致这些逻辑总被执行,哪怕前面已拒绝请求
维护模式、CORS 预检等全局拦截场景必须配对使用 ctx.StopExecution()
比如实现站点维护模式时,在 app.UseGlobal() 中检测到 maintenance.flag 存在,你写了 ctx.StatusCode(503) 和 ctx.WriteString("Service unavailable"),但若没紧跟 ctx.StopExecution(),请求仍会落入路由匹配阶段,甚至走到静态文件服务或健康检查端点。
同样,处理 CORS 预检请求时,app.Options("/api/*", ...) 的 handler 中一旦返回 204 响应,就必须立刻 ctx.StopExecution(),否则 ctx.Next() 会尝试继续匹配并执行其他中间件或路由 handler,引发重复写头错误。
- OPTIONS 路由 handler 是独立入口,不是中间件链的一部分,所以它的
ctx.StopExecution()只影响该 handler 自身生命周期 - 跨域中间件里判断
ctx.Method() == "OPTIONS"是无效的——因为 OPTIONS 请求根本不会进这个中间件,除非你显式注册了对应路由
调试中间件是否生效,优先看 ctx.StopExecution() 是否被跳过
当发现中间件“好像没起作用”:比如日志没打、鉴权没拦住、维护页面没出来,第一反应不该是重写函数,而是检查 ctx.StopExecution() 是否被遗漏、是否被写在了错误分支之外、或者是否被 ctx.Next() 覆盖。
一个简单验证方式是在中间件开头加一行日志:app.Logger().Infof("mw hit: %s", ctx.Path()),如果日志打了但后续逻辑仍执行,基本可以锁定是 StopExecution() 缺失。
- 不要依赖“写了响应就自动终止”——Iris 没有这个隐式行为
- 多个中间件串联时,任意一个中间件调了
ctx.StopExecution(),整个链都会中断,后续中间件和 handler 全部跳过 -
ctx.StopExecution()是唯一可靠的终止信号,没有替代方案


















