中间件 panic 导致 500 错误需在自定义中间件中添加 defer+recover;Use 注册须在路由前,UseGlobal 不作用于 HandleDir;ctx.Next() 被 Abort/Stop 中断则后续不执行;ctx.Values() 仅限单请求生命周期,不跨 HandleDir 生效。

中间件 panic 导致请求 500:检查 defer + recover 是否缺失
iris 中间件里如果发生未捕获的 panic(比如空指针、类型断言失败、数组越界),默认会向上抛出,最终由框架顶层处理为 500 Internal Server Error,但日志里可能只显示 “http: panic serving” 而无具体堆栈。根本原因是中间件函数本身没做 recover。
实操建议:
- 所有自定义中间件函数体最外层应包裹
defer func() { if r := recover(); r != nil { app.Logger().Errorf("middleware panic: %v", r) } }() - 避免在中间件中直接调用
panic(),改用ctx.StopWithStatus(500)或ctx.Abort()主动终止 - 若使用第三方中间件(如
iris/middleware/cors),确认其版本与当前iris/v12兼容,旧版可能未适配 v12 的上下文生命周期
中间件不执行或跳过:检查 Use / UseGlobal / WrapRouter 的调用时机和顺序
常见现象是加了 app.Use(myMiddleware) 却发现某个路由完全没进中间件,或者只对部分路径生效。这通常不是中间件逻辑问题,而是注册位置或方式不对。
实操建议:
-
Use只影响其后注册的路由;把中间件注册放在app.Get等路由定义之前,否则该路由不会被拦截 -
UseGlobal作用于所有路由(包括静态文件),但仅对HandleDir之外的路径有效;若需拦截/public/*.js,必须用WrapRouter -
WrapRouter是底层包装,它在路由匹配前执行;适合做统一鉴权或请求预处理,但不能调用ctx.Next()—— 它接收的是原生http.HandlerFunc,不是 iris.Context
ctx.Next() 不触发后续中间件:确认中间件链是否被提前 Abort 或 Stop
iris 的中间件链靠 ctx.Next() 显式推进。一旦某中间件调用了 ctx.Abort()、ctx.StopExecution() 或 ctx.StopWithStatus(),后续中间件(包括当前链上的)将不再执行,且不会自动回滚已执行的前置逻辑。
实操建议:
- 检查是否有中间件在日志打印后就调用了
ctx.Abort(),却误以为“已经执行完了” - 不要在中间件里混用
ctx.StopWithStatus(401)和ctx.StatusCode(401):前者终止链,后者只是设状态码,仍会继续执行 - 调试时可在每个中间件开头加
app.Logger().Infof("enter %s", "middleware-name"),观察哪一层之后日志消失
中间件内 ctx.Values() 数据丢失:理解 Values 生命周期范围
很多开发者期望在认证中间件里存 ctx.Values().Set("user_id", 123),然后在控制器里取,结果取不到。这不是 bug,而是 iris 的 ctx.Values() 默认只在单次请求生命周期内有效 —— 但若中间件注册在 UseGlobal,而路由又命中了静态文件服务(HandleDir),则该请求根本不会进入中间件链,Values 自然为空。
实操建议:
-
ctx.Values()数据不会跨中间件链传递给HandleDir处理器;如需全局共享,改用app.RegisterDependency注入单例对象,或用ctx.SetUserValue+ 显式传参 - 避免在中间件里修改
ctx.Request().URL或ctx.Request().Header后不调用ctx.ResetRequest(),否则后续中间件看到的仍是原始请求对象 - 若需持久化用户上下文,优先走
ctx.Session().Set()或外部缓存(如 Redis),而非依赖内存中的Values
Use 位置、ctx.Next() 调用和 Values 生效范围,比加日志更有效。


















