使用io.NopCloser复用请求体仍遇EOF错误,求排查解决
解决Go-Chi中间件中Request.Body读取后EOF的问题
问题原因
你遇到的EOF错误核心原因是:
- 替换
request.Body为io.NopCloser(bytes.NewBuffer(body))后,authzCheckRequest会读取整个请求体,此时bytes.Buffer的读取指针已移动到字节切片末尾。 - 请求传递到后续端点处理器时,再次读取
request.Body会从末尾开始,直接返回EOF。
解决方案
完成授权校验后,需要重新将request.Body重置为从开头可读的状态,修正后的代码如下:
if request.Body != nil { // 读取原始请求体并保存字节切片 body, err := io.ReadAll(request.Body) if err != nil { return nil, errors.Annotatef(err, "Failed to read request body") } // 第一次设置Body,用于授权校验 request.Body = io.NopCloser(bytes.NewBuffer(body)) // 执行授权校验 err := authzCheckRequest(request) if err != nil { w.WriteHeader(http.StatusBadRequest) return } // 校验完成后,重新设置Body,让后续处理器能从头读取 request.Body = io.NopCloser(bytes.NewBuffer(body)) } else { return nil, errors.New("empty body") } next.ServeHTTP(writer, request)
优化建议
可以封装辅助函数复用请求体复制逻辑,让代码更整洁:
// copyRequestBody 读取并复制请求体,同时恢复当前请求的Body可读状态 func copyRequestBody(r *http.Request) ([]byte, error) { if r.Body == nil { return nil, errors.New("empty body") } body, err := io.ReadAll(r.Body) if err != nil { return nil, errors.Annotatef(err, "Failed to read request body") } r.Body = io.NopCloser(bytes.NewBuffer(body)) return body, nil }
在中间件中调用:
// 复制请求体 body, err := copyRequestBody(request) if err != nil { return nil, err } // 执行授权校验 err = authzCheckRequest(request) if err != nil { w.WriteHeader(http.StatusBadRequest) return } // 重置Body供后续处理器使用 request.Body = io.NopCloser(bytes.NewBuffer(body)) next.ServeHTTP(writer, request)
关键注意点
- 每次读取请求体前,要确保
request.Body的读取指针处于起始位置;bytes.NewBuffer(body)会创建一个新的、从开头可读的缓冲区,是最稳妥的方式。 - 不要尝试直接重置现有
bytes.Buffer的位置,因为request.Body被包装成io.ReadCloser后,无法直接获取底层的bytes.Buffer实例,重新创建更简单可靠。
内容的提问来源于stack exchange,提问作者codewarrior
相关产品推荐
相关产品推荐

