iris mvc 不处理 controller 方法返回的 error,必须手动调用 ctx.stopwithstatus() 或触发 panic;否则错误被忽略,可能返回 200 状态码脏数据。

不能靠 controller 返回 error 自动捕获,必须手动触发 panic 或显式中断响应流。
为什么 controller 返回 error 不生效
Iris 的 MVC 层不会拦截 controller 方法返回的 error 值——哪怕你写成 func (c *UserCtrl) GetByID(id int64) (string, error),这个 error 也会被完全忽略。框架只认两种错误传播方式:panic 或主动调用 ctx.StopWithStatus()。
- 返回
error是 Go 函数惯用法,但在 Iris MVC 中纯属无效操作 - 不加干预时,错误逻辑会继续往下走,可能触发空指针、JSON 序列化失败或返回 200 状态码的脏数据
- 日志里也不会自动记录,除非你自己在每个方法里补
app.Logger().Errorf
推荐方案:用 ctx.StopWithStatus() + ctx.JSON() 组合
这是最可控、调试最方便的方式,避免 recover 捕获泛化 panic 导致掩盖真实崩溃点。
-
ctx.StatusCode(400)必须在ctx.JSON()之前调用,否则响应头状态码仍是 200 -
ctx.StopWithStatus(400)必须紧跟ctx.JSON()后并配return,否则 controller 剩余逻辑仍会执行 - 统一错误结构体(如
BizError)要提前定义且实现Error()方法,方便中间件识别 - 示例:
if id
备选方案:用 panic(&BizError{}) 配 recover 中间件
适合已有大量旧代码、想最小改动接入统一错误格式的场景,但要注意 panic 范围和类型判断。
- 必须用指针 panic:
panic(&BizError{...}),不能panic(BizError{...}),否则 recover 无法用类型断言识别 - recover 中间件里要严格判断
r是否为*BizError,避免把数据库连接崩溃等真异常也吞掉 - panic 后无法再写入 response body(Iris 已关闭 writer),所以只能返回固定 JSON 或重定向
- 示例中间件:
app.Use(func(ctx iris.Context) { defer func() { if r := recover(); r != nil { if bizErr, ok := r.(*BizError); ok { ctx.StatusCode(bizErr.Code / 100 * 100) // 粗略映射 40001→400,50002→500 ctx.JSON(bizErr) } else { ctx.StatusCode(500) ctx.JSON(BizError{Code: 50000, Msg: "系统异常"}) } } }() ctx.Next() })
容易被忽略的关键点
真正上线前最容易翻车的不是逻辑,而是状态码和响应头细节:
-
ctx.StopWithStatus()不等于ctx.Stop():前者会设置状态码并终止,后者只终止不设码 - 自定义错误响应中不要包含
err.Error()或堆栈,500 场景尤其要隐藏敏感信息 - 如果用了
ctx.View()渲染错误页,记得手动调用ctx.StatusCode(404),否则浏览器收到的是 200 - 所有错误路径都要走一遍
curl -I验证状态码,别只看页面内容











