You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用httptest.NewServer测试出现read on closed response body错误原因

错误发生原因
  • 你在GetData方法内部声明了defer resp.Body.Close(),该语句会在GetData方法执行结束、返回resp对象之前自动触发,直接关闭响应体。你后续在测试代码中尝试读取已经被关闭的resp.Body,就会抛出http: read on closed response body错误。
  • 程序正常运行时无报错,是因为你之前的正常业务逻辑不会在GetData返回后再读取resp.Body,所有对响应体的操作都收拢在GetData内部,所以不会触发该问题,本质是业务方法设计和调用逻辑不匹配导致的问题。
正确处理方案

根据你的业务场景可以选择以下三种方案,优先选择前两种符合Go开发规范的方案:

方案1:调整业务方法返回值,不对外暴露原始响应对象

如果上层不需要获取原始*http.Response对象,直接修改GetData方法的返回值,只返回业务处理后的数据和错误即可。所有对resp.Body的读取、解析、关闭逻辑都收拢在GetData方法内部,外部不需要接触原始响应体,从根源上避免该问题,这是最推荐的方案。

方案2:明确关闭责任归属,由调用方处理响应体关闭

如果业务逻辑确实需要对外返回原始*http.Response对象,遵循Go HTTP客户端的通用设计规范,移除GetData方法内部的defer resp.Body.Close()代码,将关闭响应体的责任交给调用方。无论是正常业务代码还是测试代码,在完成resp.Body的读取操作后,自行调用resp.Body.Close()释放资源即可。

方案3:在业务方法内重置响应体(临时兼容方案)

如果既需要保留GetData内部关闭响应体的逻辑,又需要外部可以读取返回的resp.Body内容,可以在GetData内部读取完响应体内容后,重置resp.Body再返回:

import (
    "bytes"
    "io"
    "net/http"
)

func GetData(url string) (*http.Response, error) {
    resp, err := http.Get(url)
    if err != nil {
        return nil, err
    }
    // 读取原始Body内容
    bodyBytes, err := io.ReadAll(resp.Body)
    if err != nil {
        resp.Body.Close()
        return nil, err
    }
    // 关闭原始Body
    resp.Body.Close()
    // 重置Body为可重复读取的缓冲区,且自带空Close实现
    resp.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
    return resp, nil
}

该方案下返回的resp.Body是新构造的缓冲区,没有被关闭,外部可以正常读取,不需要额外关闭也不会有资源泄漏问题。


内容的提问来源于stack exchange,提问作者CJW

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 10:24:10