recover中间件必须用app.use是因为它需在请求进入handler前挂载到执行链上,仅在handler内defer recover只能捕获该函数内panic,无法拦截中间件或路由层panic。

为什么 recover 中间件必须用 app.Use 而不能只写在 handler 里
Iris 的 recover 中间件本质是 panic 捕获器,它必须在请求进入 handler 前就挂载到执行链上。如果只在某个 app.Get("/path", handler) 的 handler 函数内部用 defer recover(),那只能捕获该函数内的 panic;一旦中间件或上层路由逻辑 panic(比如参数解析失败、中间件自身 bug),整个服务就会崩溃——Iris 不会自动 recover。
实操建议:
-
app.Use(recover.New())是最简方式,但仅处理 panic,不处理业务错误(如数据库超时、校验失败) - 若需统一处理
error,得自己写中间件,在ctx.Next()后检查ctx.Values().Get("error")或约定返回结构 - 不要把
recover.New()放在app.Done()里——Done是响应后执行,panic 已经发生且无法拦截
自定义错误中间件怎么区分 panic 和业务 error
真实场景中,你既怕 map panic,也怕 ctx.ReadJSON(&v) == nil 后没校验直接查库。一个中间件得同时兜住两类问题。
常见错误现象:前端收到 500,日志里却只打印 "runtime error: invalid memory address",完全看不出是哪条路由、哪个参数出的问题。
实操建议:
- 先调
app.Use(recover.New())捕获 panic,并用ctx.Application().Logger().Errorf记录堆栈 - 再写一层中间件,在
ctx.Next()后检查ctx.Response().StatusCode()是否为 0(说明 handler 没设状态码,可能 panic 了)或是否手动设置了 500 - 业务 error 推荐统一用
ctx.StopWithStatus(400, "bad request")或ctx.JSON(400, map[string]string{"error": "xxx"}),避免混用panic和return - 别在中间件里调
ctx.ReadBody()——它会消费 body,后续 handler 再读就是空的
ctx.Stop() 和 ctx.StopWithStatus() 的关键区别
很多中间件(尤其是鉴权类)需要提前终止流程,但选错方法会导致状态码错乱或响应体缺失。
实操建议:
-
ctx.Stop()只中断执行链,不改状态码——如果之前 handler 已设过 200,这里 stop 后前端仍收到 200 + 空响应体 -
ctx.StopWithStatus(401)会立即设置状态码并中断,但不会自动写响应体;必须紧接着调ctx.JSON(401, ...)或ctx.Text(...),否则前端收不到内容 - 若想返回标准错误格式,建议封装:
func AbortWithError(ctx iris.Context, status int, msg string) { ctx.StopWithStatus(status); ctx.JSON(status, map[string]string{"error": msg}) } - 永远别在
go func()里调ctx.Stop()——ctx不跨 goroutine 安全
生产环境日志里看不到 panic 堆栈?检查这三点
即使加了 recover.New(),线上日志仍可能只有 "Internal Server Error",没有具体文件行号。
容易踩的坑:
-
DisableInterruptHandler: true配置会关闭默认 panic 日志输出,必须显式传iris.WithConfiguration(iris.Configuration{DisableInterruptHandler: false}) - 日志中间件(如
logger.New())若启用了LogRequestBody: true,会在 panic 前就尝试读 body,导致 recover 失效 - 自定义
recover中间件里忘了调ctx.Application().Logger().Errorf("%+v", err),只打印了简单字符串,丢失堆栈
最稳妥的做法是:用 recover.New(recover.WithVerbose(true)),并确保 logger 在 recover 之后才初始化。











