自定义Golang ServeHTTP记录响应状态码时遇空指针引用错误
问题分析
错误invalid memory address or nil pointer dereference是因为你尝试访问req.Response.StatusCode,但服务端处理的*http.Request对象里,Response字段是nil——这个字段只有客户端发起请求(比如用http.Client)时才会被填充,服务端接收的请求里根本没有这个值,直接访问必然触发空指针 panic。
解决方案:自定义ResponseWriter记录状态码
要获取响应状态码,你得自己包装http.ResponseWriter,在写入响应时记录状态码,具体步骤如下:
- 定义嵌入
http.ResponseWriter的自定义结构体,用来记录状态码:
type ResponseRecorder struct { http.ResponseWriter statusCode int } func NewResponseRecorder(w http.ResponseWriter) *ResponseRecorder { // 默认状态码设为200,因为未显式调用WriteHeader时,响应默认状态就是200 return &ResponseRecorder{w, http.StatusOK} } // 重写WriteHeader,记录状态码 func (rr *ResponseRecorder) WriteHeader(code int) { rr.statusCode = code rr.ResponseWriter.WriteHeader(code) } // 处理直接调用Write但未调用WriteHeader的情况,确保状态码正确 func (rr *ResponseRecorder) Write(b []byte) (int, error) { if rr.statusCode == 0 { rr.statusCode = http.StatusOK } return rr.ResponseWriter.Write(b) }
- 修改
ServeHTTP函数,用自定义的ResponseRecorder包装原w,日志记录时用记录的状态码:
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { handler := r.getHandler(req.Method, req.URL.Path) if handler == nil { notFoundHandler(w, req) return } // 用自定义ResponseRecorder包装原响应写入器 rr := NewResponseRecorder(w) reqStruct := newReqStruct(rr, req) // 确保newReqStruct兼容http.ResponseWriter接口即可 // 这里的goroutine完全多余——启动后立刻阻塞等待done,和同步调用没区别,直接去掉简化代码 handler(reqStruct) // 用记录的状态码打日志 logger.logRequest(req.URL.Path, req.Method, rr.statusCode) }
额外注意
- 确认
newReqStruct的参数是http.ResponseWriter接口类型,这样自定义的ResponseRecorder可以直接传入,因为它实现了该接口。 - 原代码里的goroutine和chan属于冗余代码,完全可以删除,不会影响功能,还能减少不必要的并发开销。
内容的提问来源于stack exchange,提问作者Folium
相关产品推荐
相关产品推荐

