Go HTTP handler 中 panic 默认导致500且不记录堆栈,需手动 recover 并调用 debug.PrintStack;error 返回不等于HTTP状态码,须显式设置;错误响应应统一结构并过滤敏感信息。
Go HTTP handler 里 panic 会直接 500,但默认不记录堆栈
Go 的
不会自动 recover panic,一旦 handler 内部 panic,连接就直接断开,客户端只看到空响应或 500,日志里也没线索。这不是“没报错”,是错被吞了。
必须手动在中间件或顶层 handler 包一层
,否则所有 panic 都静默失败
只在 defer 中有效,且仅对当前 goroutine 生效 —— 别指望它能抓到子 goroutine 里的 panic
别只打印
,要调用
或用
拿完整堆栈,否则定位不到哪行出的错
示例:在自定义中间件中写
error 返回 ≠ HTTP 错误状态码,别混淆语义
Go 函数返回
是控制流的一部分,和 HTTP 状态码无关。
或
才真正决定响应头里的
。常见错误是:函数返回了
,但 handler 里没调用
,结果还是 200 + 错误文本。
显式错误类型比字符串判断更可靠:定义
,在中间件统一检查并设置状态码
避免在业务逻辑里直接调用
—— 这会让 handler 失去控制权,也难测试;应让 handler 自己决定如何渲染错误
如果用了 Gin/Echo 等框架,它们的
或
是封装好的,但底层仍依赖你正确传递状态码,不是靠 error 值自动推导
全局错误响应格式不统一,前端解析容易崩
后端返回
、
、甚至纯文本,前端得写一堆条件分支来处理,稍有遗漏就 crash 或静默失败。
go语言参考手册 中文CHM版
Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。本文给大家带来Go参考手册,需要的可以来下载! Go是从2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ian Lance Taylor, Russ Cox等人,并最终于2009年11月开源,在2012年早些时候发布了Go 1稳定版本。现在Go的开发已经是完全开放的,并且拥有一个活跃的社区。 Go 语言特色 简洁、快速、安全 并行、有趣、开源 内存管理、v数组安全、编译
下载
强制所有错误响应走同一结构体,比如:
在中间件统一序列化错误,不要每个 handler 都
一遍;尤其注意 content-type 必须设为
400/401/403/404/500 这些状态码对应不同语义,别全用 500 —— 例如参数校验失败该用 400,未登录该用 401,权限不足该用 403
生产环境别暴露
或原始 error message 给前端,敏感信息(如数据库表名、路径)必须过滤
panic 恢复后继续写响应可能 panic,w.Header().Set() 会报错
recover 之后,
的状态可能已部分写入(比如 header 已发),此时再调用
或
会 panic 报
或
。
立即学习
“
go语言免费学习笔记(深入)
”;
恢复 panic 后第一件事是检查
是否为空,或者用
包装器提前拦截写操作(推荐 go-chi/render 等成熟方案)
更稳妥的做法:recover 后立即构造新响应,用
替换原
,确保不会二次写入
别在 defer 里做耗时操作(如写日志到网络服务),panic 恢复路径本就不可靠,再加 IO 失败会导致错误进一步掩盖
事情说清了就结束。真正麻烦的不是怎么写 recover,而是 panic 发生时请求上下文(如 trace id、user id)是否还在,以及错误是否被正确分类进监控系统 —— 这些往往被忽略,却决定了线上问题能不能快速归因。
http.ServeHTTPrecover()recover()errdebug.PrintStack()runtime/debug.Stack()func Recovery(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
log.Printf("PANIC in %s %s: %+v", r.Method, r.URL.Path, err)
debug.PrintStack()
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}()
next.ServeHTTP(w, r)
})
}errorhttp.Error()w.WriteHeader()Statusfmt.Errorf("not found")w.WriteHeader(http.StatusNotFound)type AppError struct { Code int; Err error }http.Error()c.AbortWithStatusJSON()c.Error(){"error": "xxx"}{"message": "xxx", "code": 1001}type ErrorResponse struct {
Code int `json:"code"`
Message string `json:"message"`
TraceID string `json:"trace_id,omitempty"`
}json.Marshal()application/jsonruntime.Callerhttp.ResponseWriterw.WriteHeader()w.Write()http: multiple response.WriteHeader callshttp: superfluous response.WriteHeader callw.Header().Get("Content-Type")struct{ http.ResponseWriter } io.DiscardResponseWriter