Iris 默认 panic 会终止整个 HTTP server,因其无自动 recover 机制且每个请求在独立 goroutine 中执行;必须手动前置注册 iris.Recover 中间件,并可自定义错误响应体,但 recover 仅兜底未预期 panic,不能替代业务层错误处理。

Iris 没有自动 panic 捕获机制,不加 recover 中间件时,任何未处理 panic 都会导致整个 HTTP server 崩溃退出。
为什么默认 panic 会杀掉整个服务
Iris 的 ctx.Next() 只在当前 goroutine 内有效,它不会自动包装 handler 函数做 recover。Go 的 panic 默认只被当前 goroutine 捕获,而每个 HTTP 请求由独立 goroutine 处理——如果 handler 内部 panic 且没被拦截,该 goroutine 就终止,但 Iris 不会主动 recover,最终进程收到未捕获 panic 后直接退出。
常见触发点:数据库查询返回 nil 后直接解引用、json.Unmarshal 传入未初始化结构体指针、调用空接口方法等。
- 别依赖
iris.Default()自带 recover —— 它只是个快捷封装,实际仍需显式启用 - 所有自定义中间件、handler、甚至模板渲染中都可能 panic,必须统一入口拦截
- 日志里看到
panic: runtime error: invalid memory address并伴随服务重启,基本就是漏了 recover
recover 中间件必须手动注册且放最前
中间件执行顺序决定 recover 能否生效:app.Use(iris.Recover) 必须在 app.Get 等路由注册之前调用,否则 panic 发生在中间件链之外,recover 根本不执行。
错误写法示例:
app.Get("/api/user", handler)
app.Use(iris.Recover) // ❌ 生效不了,路由已注册完
正确结构:
- 始终把
app.Use(iris.Recover)放在app.Get/app.Post之前 - 若用了分组(
app.Party("/api")),需在分组对象上调用Use,而非根 app - 不要自己写裸
defer func() { if r := recover(); r != nil { ... } }()——iris.Recover已处理日志、状态码(500)、响应格式化,重复造轮子容易漏掉 Content-Type 或响应体
自定义错误响应体要兼容前端解析
iris.Recover 默认返回纯文本 "Internal Server Error",前端 fetch 时 response.json() 会失败。需要替换其内部的错误处理器。
实操方式是传入自定义函数:
app.Use(iris.Recover(func(ctx iris.Context, err interface{}) {
ctx.StatusCode(500)
ctx.JSON(iris.Map{
"code": 500,
"message": "Internal server error",
"detail": fmt.Sprintf("%v", err),
})
}))
- 注意:这个函数必须接收两个参数
(ctx iris.Context, err interface{}),少一个或类型不对都会编译失败 -
err是原始 panic 值,可能是string、error接口或自定义结构体,用fmt.Sprintf("%v", err)最安全 - 生产环境建议关闭
detail字段,避免泄露堆栈或敏感路径信息 - 别在该函数里再调用
ctx.Next()—— recover 已终止当前链,继续执行无意义
recover 不能替代业务层错误处理
recover 只兜底未预期 panic,不是用来吞掉 if err != nil 的占位符。比如数据库查不到用户,应该返回 404 + 明确 message;若因忘记判空导致 user.Name panic,recover 虽能保服务不挂,但前端拿到的是 500,语义完全错乱。
真正要盯紧的是这些地方:
- 所有
ctx.ReadJSON(&v)后必须检查err != nil,并主动返回 400 - 调用外部 API 或 DB 查询后,先判
err再访问返回值字段 - 模板中避免直接写
{{.User.Name}},应改为{{with .User}}{{.Name}}{{end}}防止 nil 指针 - 中间件中调用
ctx.User.Get()前,确认前面鉴权逻辑已成功执行并设置了用户
recover 是最后的安全网,不是免检通道。越底层的 panic 越难 debug,留到这一步才捕获,说明前面的防御已经漏了一大截。


















