Echo中间件无法直接获取真实HTTP状态码,因c.Response().Status仅为缓存且更新滞后;必须用echo.NewWrapResponseWriter包装响应体并监听WriteHeader(),在defer中调用rw.Status()获取最终写出的状态码。

中间件里拿不到 http.ResponseWriter 原始状态码?
Echo 的 echo.Context 默认不暴露已写入的 HTTP 状态码,因为底层 http.ResponseWriter 的 WriteHeader() 调用是延迟的——直到响应真正写出(比如调用 c.String()、c.JSON())才触发。中间件执行时,多数 handler 还没写状态码,直接读取会得到 0 或默认值。
用 echo.WrapResponseWriter 拦截并记录状态码
必须包装响应体,监听 WriteHeader() 调用。Echo 提供了 echo.WrapResponseWriter 接口和 echo.NewWrapResponseWriter 工厂函数,这是唯一可靠方式。
常见错误:试图在中间件末尾用 c.Response().Status ——它返回的是当前内部状态字段,不是实际写出的码;若 handler 没显式调用 WriteHeader 或使用了 c.NoContent(404) 等快捷方法,该字段可能未更新或滞后。
- 必须在中间件开头就用
echo.NewWrapResponseWriter包装c.Response() - 包装后需调用
c.SetResponse()替换原始响应体,否则后续 handler 仍操作原对象 - 重写
WriteHeader()方法,在其中保存状态码到上下文(如c.Set("status", status))或外部变量 - 注意并发安全:每个请求独立一个中间件实例,但若把状态存到全局 map 里,需加锁
func StatusLogger() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
rw := echo.NewWrapResponseWriter(c.Response(), c.Request().ProtoMajor)
c.SetResponse(rw)
// 记录状态码
defer func() {
if status := rw.Status(); status > 0 {
log.Printf("path=%s status=%d", c.Path(), status)
}
}()
return next(c)
}
}
}
为什么不能用 c.Response().Status 直接读?
因为 echo.Response.Status 字段只在 WriteHeader() 被显式调用时更新,而很多 Echo 内置方法(如 c.JSON(200, data))内部先写状态再写 body,但中间件执行时机早于这些调用。更关键的是:这个字段是 Echo 自己维护的缓存,并非从底层 http.ResponseWriter 同步读取,存在竞态和延迟。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
-
c.JSON(400, err)会在 handler 执行时才设置Status字段,中间件此时读仍是 0 - 如果 handler panic 导致 Echo 自动返回 500,这个 500 不会写入
c.Response().Status,只有包装后的rw.Status()能捕获到 -
rw.Status()是最终实际写出的状态码,真实反映客户端收到的值
注意 defer 的执行时机和日志位置
状态码只能在响应真正结束之后才能确定,所以获取逻辑必须放在 defer 里,或者在 next(c) 返回之后。但别在 defer 里访问 c.Request() 或其它可能已被释放的对象——Echo 在 handler 返回后会复用 context 实例。
- 不要在
defer中调用c.Path()或c.Param(),它们可能已失效 - 推荐在
defer中只读取你提前存好的值(如用c.Set("req_path", c.Path())),或用rw.Status()这种不依赖 context 生命周期的方法 - 如果要记录耗时,也要在
defer里用time.Since(start),且 start 时间戳必须在中间件开头就记录
最易忽略的一点:包装器必须在所有其他中间件之前注册,否则下游中间件可能已操作过原始响应体,导致状态码丢失或重复写入。









