在 golang 中多次请求后多次关闭响应体

Close response body multiple times after multiple requests in golang

, it is pointed out that response.Body should be closed to avoid resource leak. It is also shown in the overview examples in http package godoc.

在我的测试代码中,我发送了多个请求来尝试 API 和

resp, err := http.DefaultClient.Do(req)

在同一个函数中多次。这是一种不好的做法吗?在这种情况下,我是在每个之后写 defer resp.Body.Close(),还是只写一次?

url := server.URL + "/ticket/add"                                       
reader = strings.NewReader(`{"id": "test1", "detail": "test1"}`)        
req, err := http.NewRequest("POST", url, reader)                        
assert.Nil(t, err)               

resp, err := http.DefaultClient.Do(req)                                 
assert.Nil(t, err)                                                      

defer resp.Body.Close()                                                 

assert.Equal(t, http.StatusCreated, resp.StatusCode)                    
// add a ticket with same id                                            
reader = strings.NewReader(`{"id": "test1"}`)                           
req, err = http.NewRequest("POST", url, reader)                         
assert.Nil(t, err)                                                      

resp, err = http.DefaultClient.Do(req)                                  
assert.Nil(t, err)                                                      
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)   

一个相关的问题,在服务端,也就是在func(w http.ResponseWriter, r *http.Request)里面,是否也需要关闭request body?

是的,您需要关闭两个回复。推迟对 resp.Body.Close 的一个调用不会以某种方式影响另一个。 *http.Response 各不相同,都可以延期。

在服务器端,您不需要关闭 Request.Body -- 从 http.Request documentation:

// The Server will close the request body. The ServeHTTP
// Handler does not need to.

不好的做法

如果您没有重用 resp 变量,很明显您正在处理不同的响应实例,每个响应实例都必须关闭。

Do sends an HTTP request and returns an HTTP response (instance)...

所以,不好的做法不是发出多个请求,而是对多个响应重复使用同一个变量。它导致代码不明显。并生成永远无法完成的无法访问的对象。

延迟执行

A defer statement pushes a function call onto a list. The list of saved calls is executed after the surrounding function returns.

如果您计划了单个或多个 deferred 引用同一变量的执行,则只会执行最后分配给变量对象的方法(多次 defers)。

Playground with an example

关闭Response.Body

来自 Response documentation:

It is the caller's responsibility to close Body.

因此,通常您必须关闭每个 Response.Body

垃圾收集和终结(编辑)

垃圾收集器调用绑定到收集对象终结器以关闭文件、连接和执行其他清理操作。并且默认情况下没有终结器绑定到 Body 对象。

您的改进代码段:

// ...a lot of code
resp_one, err := http.DefaultClient.Do(req)                                 
 // BTW, `assert.Nil` just returns whether the assertion was successful (bool) not terminates a test.
if assert.Nil(t, err) == true {
    defer resp_one.Body.Close()                                                 
}

// ...a lot of code
resp_two, err = http.DefaultClient.Do(req)                                  
if assert.Nil(t, err) == true {
    defer resp_two.Body.Close()
}