Iris无全局异常处理器,需用app.UseRouter中间件+defer recover()捕获panic;ctx.Next()须在defer块后调用;业务错误应主动设状态码而非panic;模板panic需单独处理。

iris.GlobalExceptionHandler 是伪概念,实际靠 app.UseRouter + ctx.Next() 拦截
Iris 没有类似 Spring 的 @ControllerAdvice 或 Express 的全局 error handler 机制。所谓“全局异常处理”,本质是利用中间件在路由匹配后、handler 执行前/后插入逻辑,再配合 ctx.StatusCode() 和 ctx.StopExecution() 控制流程。最常见错误是试图在 app.Use() 里直接 recover panic —— 这根本捕不到 HTTP handler 内部 panic,因为 Iris 的 handler 是异步调度进 goroutine 的,recover() 必须和 panic() 在同一 goroutine。
-
app.UseRouter()是唯一能稳定拦截所有匹配路由的中间件入口,它在路由解析完成、handler 调用前执行 - 必须手动 wrap handler 函数,用
defer+recover()包裹原始 handler 调用,否则 panic 会直接冒泡到 http.Server - 不要依赖
app.OnErrorCode(500)处理业务 panic,它只响应显式调用ctx.StatusCode(500)的场景,对 panic 无效
标准 panic 捕获模板:用 app.UseRouter 包一层 wrapper
以下是最小可行写法,可直接复用:
app.UseRouter(func(ctx iris.Context) {
defer func() {
if r := recover(); r != nil {
// 记录日志(建议用 ctx.Application().Logger())
ctx.Application().Errorf("Panic recovered: %v", r)
// 返回统一错误页或 JSON
ctx.StatusCode(500)
ctx.JSON(iris.Map{"error": "internal server error"})
ctx.StopExecution() // 关键:阻止后续 handler 执行
}
}()
ctx.Next() // 继续执行匹配到的 handler
})
注意:ctx.Next() 必须放在 defer 块之后,否则 panic 发生时 ctx.Next() 已执行完毕,recover() 就失效了。
业务错误不推荐 panic,改用 ctx.StopExecution() + 自定义状态码
比如数据库查询失败、参数校验不通过等,应主动终止流程而非触发 panic。Iris 提供更轻量的控制方式:
- 在 handler 内部调用
ctx.StatusCode(400)或ctx.StatusCode(422),再返回结构化错误信息 - 配合
app.OnErrorCode(400)注册专门的 400 错误处理器,用于渲染错误页或统一日志 - 避免在中间件里对每个请求都做
recover(),性能有损耗;仅在顶层 router 中间件做一次即可
真正需要 panic 的场景极少,比如初始化阶段配置加载失败,或不可恢复的底层库错误 —— 这类应提前暴露,不该等到请求时才 panic。
容易被忽略的细节:panic 可能来自模板渲染或视图层
如果你用了 app.RegisterView(),HTML 模板中执行 Go 代码(如 {{.Data.Method()}})也可能 panic。这种 panic 不在 handler 函数 goroutine 内,上述 UseRouter wrapper 无法捕获。
- 解决方案:启用
iris.WithoutVersionChecker并自定义view.Engine,重写ExecuteWriter方法,在其中加defer recover() - 更务实的做法:模板内避免复杂逻辑,把计算移入 handler,只做简单渲染
- 测试时用
ctx.View("xxx.html")单独跑一遍模板,比上线后才发现崩溃更可靠
异常处理不是兜底,而是分层控制:参数校验错用 400,业务规则错用 409,系统级故障才用 500 —— panic 只该是最后防线,且必须确保它真能被 catch 到。


















