在处理http request的时候,偶然发现,body读取之后想再次读取的时候,发现读不到任何东西。见下方代码:
req err = io.ReadAll(request.Body)
if err != nil {
log.Println("io ReadAll failed :", err.Error())
return
}
之后如果想再次io.ReadAll(request.Body)的时候会发现读到的是空。于是我决定去看一下这个resp.Body,发现它是一个io.ReadCloser接口,包含了Reader和Closer接口:
type ReadCloser interface {
Reader
Closer
}
于是我想到了文件,它也实现了io.Reader接口,所以用读文件试了下:
func readFile(path string)string{
fi,err := os.Open(path)
if err != nil{panic(err)}
defer fi.Close()
byte1,err := io.ReadAll(fi)
fmt.Println(string(byte1))
byte2,err := io.ReadAll(fi)
fmt.Println(string(byte2))
return string(fd)
}
发现结果是一致的,fmt.Println(string(fd2))打印不出任何结果。我猜测应该是ioutil.ReadAll()是有记录偏移量,所以会出现第二次读取读不到的情况。作为client端处理response的时候会碰到这个问题,作为server端要处理request body的时候,一样会遇到此问题,那么该如何解决这个问题呢?
有一个方法是再造一个io.ReadCloser,如下:
fi2:= io.NopCloser(bytes.NewBuffer(byte1))
byte3,err := io.ReadAll(fi2)
fmt.Println(string(byte3))
此外,作为client端处理response的时候,有一点要注意的是,body一定要close,否则会造成GC回收不到,继而产生内存泄露。其实在go的官方源码注释中,也明确注明了response body需要调用方进行手动关闭:It is the caller's responsibility to close Body.
至于response body为什么需要进行关闭,这篇文章进行了解释:
https://studygolang.com/articles/9887
那么作为client端生成的request body,需不需要手动关闭呢,答案是不需要的,net/http中的func (c *Client) Do(req *Request) (*Response, error)会调用Close()。
同样的,作为server端接收的request body,也是需要关闭,由Server自动进行关闭,The Server will close the request body. The ServeHTTP Handler does not need to.