全局错误处理器必须在app.userouter()之后注册,用app.onerrorcode或app.onanyerrorcode捕获404/500;中间件内错误需调用ctx.stopwitherror()才能进入全局处理链,不能仅设状态码或panic。

全局错误处理器必须用 app.UseRouter() 之后注册
IRIS 的全局异常处理不是靠中间件链的任意位置生效,而是依赖路由匹配后的兜底机制。如果你在 app.Use() 里注册错误处理器,它会被当作普通中间件提前执行,无法捕获路由未匹配或 handler panic 导致的 404/500;必须改用 app.UseRouter()(注意是 Router,不是 Use)之后调用 app.OnErrorCode() 或 app.OnAnyErrorCode()。
常见错误现象:app.Use(func(ctx iris.Context) { ... }) 里 recover() 捕不到 panic,或者 app.OnErrorCode(500, ...) 完全不触发——大概率是注册顺序错了。正确顺序是:注册所有路由 → 调用 app.UseRouter() → 再注册 OnErrorCode。
-
app.OnErrorCode(404, func(ctx iris.Context) { ctx.HTML("页面没找到") })只捕获显式返回的 404,比如ctx.StatusCode(404) -
app.OnAnyErrorCode(func(ctx iris.Context) { ... })才能兜住 panic、未注册路由、JSON 解析失败等所有未被捕获的错误 - 若启用了
iris.DevMode,部分底层错误(如模板语法错)会绕过自定义处理器,直接输出调试堆栈
panic 恢复必须在 OnAnyErrorCode 里做,不能靠 defer+recover
IRIS 的请求生命周期中,handler panic 后框架会自动终止当前上下文,但不会自动 recover。你不能在 handler 内部写 defer func(){if r := recover(); r != nil { ... }}() 来处理——这只能捕获本函数内 panic,对中间件或依赖注入失败等外部 panic 无效。真正可靠的恢复点只有 app.OnAnyErrorCode() 回调内部。
这个回调函数接收的是已封装好的错误上下文,ctx.GetStatusCode() 返回实际状态码,ctx.Values().Get("error") 可取到原始 panic 值(类型为 interface{}),需断言判断是否为 error 或字符串。
- 不要在
OnAnyErrorCode里调用ctx.Next(),它已无意义 - 若需记录 panic 堆栈,用
debug.PrintStack(),别依赖ctx.Application().Logger()的默认格式,它可能截断长消息 - 避免在该回调里做耗时操作(如写磁盘日志),否则会阻塞整个错误响应流程
自定义错误页要区分开发与生产环境
IRIS 不像某些框架自动根据 GO_ENV 切换错误页。你必须手动检查 ctx.Application().IsDevelopment(),再决定返回精简 HTML 还是带堆栈的纯文本。
例如 500 错误页:开发时返回 ctx.JSON(iris.Map{"error": err, "stack": stack}) 方便调试;生产时只返回 ctx.View("500.html") 并记录日志。注意 ctx.View() 需提前通过 app.RegisterView() 注册模板引擎,且路径必须存在,否则会二次 panic。
- 静态错误页(如 404.html)建议放在
./web/errors/目录,用app.StaticWeb("/errors", "./web/errors")暴露,避免模板引擎加载失败导致错误页也挂掉 - 若用 JSON API,统一返回
ctx.JSON(iris.Map{"code": 500, "message": "服务暂时不可用"}),别混用 HTML 和 JSON - HTTP/2 环境下,错误响应头中的
Content-Type必须与实际 body 匹配,否则客户端可能解析失败
中间件级错误需主动传递给全局处理器
中间件里发生的错误(如 JWT 验证失败、DB 连接超时)默认不会触发 OnAnyErrorCode,因为 IRIS 认为这是中间件自己的控制流。你得显式调用 ctx.StopWithStatus(401) 或 ctx.StopWithError(500, err),才能让错误进入全局处理链。
ctx.StopWithError() 是关键:它把错误注入上下文,并设置状态码,后续才会被 OnAnyErrorCode 捕获。直接 return 或 ctx.StatusCode() 不行,前者中断中间件但不通知框架,后者只改状态码不传错误值。
- JWT 中间件示例:
if err != nil { ctx.StopWithError(401, errors.New("token invalid")) } - 数据库中间件里,
if db == nil { ctx.StopWithError(503, errors.New("db unavailable")) } - 避免在中间件里用
panic(err),这会导致整个 goroutine 崩溃,而非优雅降级
StopWithError 的必要性——很多开发者以为设了状态码就完事,结果错误静默丢失。











