
martini 不支持直接用 `http.handler` 类型中间件,需改用 `martini.context` 和 `c.next()` 实现 panic 全局恢复;推荐优先使用内置的 `martini.recovery`,或自定义 `recoverwrap` 中间件并确保其注册顺序最靠前。
Martini 的中间件机制与标准 net/http 不同:它基于依赖注入和上下文链式调用(c.Next()),而非 HTTP handler 包装。因此,将 RecoverWrap 声明为 func(http.Handler) http.Handler 会导致编译失败——Martini 的 injector 无法识别该签名,也无法将其注入到路由执行流程中。
正确做法是定义一个接收 martini.Context 和 http.ResponseWriter 的函数,并在 defer 中调用 recover(),最后显式调用 c.Next() 触发后续处理:
func RecoverWrap(c martini.Context, w http.ResponseWriter) {
defer func(w http.ResponseWriter) {
if r := recover(); r != nil {
var err error
switch v := r.(type) {
case string:
err = errors.New(v)
case error:
err = v
default:
err = errors.New("unknown panic value")
}
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
// 可选:记录日志,例如 log.Printf("Panic recovered: %+v", err)
}
}(w)
c.Next()
}⚠️ 关键注意事项:
- 必须将 RecoverWrap 通过 m.Use(RecoverWrap) 最早注册(即在任何其他中间件或路由之前),否则在其之前 panic 的代码将无法被捕获;
- c.Next() 是核心:它代表“继续执行后续中间件和最终路由处理器”,所有 panic 都应发生在 c.Next() 调用期间;
- 不要尝试在 RecoverWrap 内部调用 h.ServeHTTP(...)——Martini 没有 http.Handler 入参,也不走标准 HTTP handler 链。
实际上,Martini 已内置成熟实现:martini.Recovery。它不仅捕获 panic,还支持可选的日志输出(默认启用)和自定义错误响应。推荐直接使用:
m := martini.Classic()
m.Use(martini.Recovery()) // ✅ 开箱即用,生产环境首选
m.Get("/", func() {
panic("boom!")
})如需定制错误响应(如返回 JSON 错误),可组合 martini.Recovery 与自定义 martini.Logger,或封装自己的 recovery 中间件——但务必继承其安全逻辑(如禁止暴露 panic 堆栈给客户端)。总之,避免重复造轮子;优先使用官方 Recovery,仅在有特殊需求时才扩展。










