在哪里放置“defer req.Body.Close()”?

38
我有一些使用 net/http 模块编写的 web 服务器处理程序,每个处理程序中都有 defer req.Body.Close() 语句。请问这段代码应该放在哪个位置?是在函数末尾还是开头无所谓呢?

如果您将 defer req.Body.Close() 放在最后,您就不需要使用 defer 关键字,但是您会冒着您或任何未来的程序员意外添加代码从而提前从函数返回的风险,那么 req.Body.Close() 就不会运行。 - nos
这个问题表述不够明确,你是在问关于 http.Handler 在服务器端处理传入请求的情况,还是在客户端向其他服务器发出传出请求的情况? - icza
@JimB 那其实就是答案,发表出来吧。 - icza
@icza:我本以为会找到一个重复的,但没那么幸运 ;) - JimB
5个回答

84

在处理程序中,请求体不需要被关闭。来自http.Request文档

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

11

net/http

服务器请求

2ede818中澄清,net/http声明:

对于服务器请求,请求体(Request Body)始终非空,但当没有请求体时将立即返回EOF。 服务器将关闭请求体,ServeHTTP处理程序不需要这样做。

也就是说,在 ServeHTTP 处理程序的调用堆栈中,您不应该关闭请求主体。 - spongecaptain

9
这个应该放在哪个位置?我应该把它放在函数的结尾还是一开始就行了,这是否真的很重要呢?
都不对。两种方法都极其错误。 defer req.Body.Close() 已经成为了惯例。
首先让我们看看一些硬性的事实:
  1. 如果请求失败(返回非 nil 的错误),则没有 Body 可以关闭,无论是通过延迟(defer)方式还是直接方式。

  2. 您必须在可能涉及到的所有代码路径上关闭 Body(如果存在)。

  3. 在处理 Body(或至少处理部分 Body)之前,您可能不想关闭它。

现在回到你的问题选项:
  • "在函数开头": 这完全是错误的,因为 Body 可能为空(事实1)。

  • "在函数结尾": 完全错误,因为A) 如果您错过了一个离开函数的代码路径(事实2),那么它就会变得很危险。B) 即使您在函数结束时加上 defer Bod.Close(),也完全没有意义,与直接通过 Body.Close() 关闭它相比,延迟关闭毫无用处。

唯一合理的延迟关闭请求主体的方法是在确认 Body 非空后立即执行,这意味着请求未返回错误。

18
我会将其翻译为:我只是简单地放在这里:“对于服务器请求,请求正文始终不为空”。 - Denis V
7
此答案仅适用于“客户端”发起的HTTP请求。服务器端(如问题所问)无需关闭连接。(但将此答案应用于客户端时是有用的。) - user2679859
@DenisV,除非您正在测试控制器并创建自己的请求,但是忽略向请求添加正文(例如使用http.NewRequest("some method", "some path", nil) - TwiN

5

如文档所述,无需在客户端和服务器端都显式关闭它。

// Body is the request's body. 
// 
// For client requests, a nil body means the request has no 
// body, such as a GET request. The HTTP Client's Transport 
// is responsible for calling the Close method. 
// 
// For server requests, the Request Body is always non-nil 
// but will return EOF immediately when no body is present. 
// The Server will close the request body. The ServeHTTP 
// Handler does not need to. 

2
根据Go的文档,当你完成使用时,关闭主体是由你来决定的。
我通常会在检查请求是否有错误的代码行之后立即添加defer语句。

5
此回答仅适用于“客户端”发起的HTTP请求。对于服务器端(如问题所问),不需要关闭连接。 - user2679859

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接