Reuse for requests other than Get
package main
import (
"net/http"
"strings"
)
func main(){
reader := strings.NewReader("hello")
req,_ := http.NewRequest("POST","http://www.abc.com",reader)
client := http.Client{}
client.Do(req) // First request will succeed
client.Do(req) // Request fails
}
The second request will return an error
http: ContentLength=5 with Body length 0
The reason is that after the first request, req.Body has been read to the end, so the second request cannot read the body again. Solution: Redefine a implementation class of ReadCloser to replace req.Body
package reader
import (
"io"
"net/http"
"strings"
"sync/atomic"
)
type Repeat struct{
reader io.ReaderAt
offset int64
}
// Read Rewrite the read method so that each time request.Body is read, it can be read from the specified position
func (p *Repeat) Read(val []byte) (n int, err error) {
n, err = p.reader.ReadAt(val, p.offset)
atomic.AddInt64(&p.offset, int64(n))
return
}
// Reset Reset offset
func (p *Repeat) Reset(){
atomic.StoreInt64(&p.offset,0)
}
func (p *Repeat) Close() error {
// Because req.Body implements the readcloser interface, the close method must be implemented
// But the value in repeat may be read-only, so here just try to close it.
if p.reader != nil {
if rc, ok := p.reader.(io.Closer); ok {
return rc.Close()
}
}
return nil
}
func doPost() {
client := http.Client{}
reader := strings.NewReader("hello")
req , _ := http.NewRequest("POST","http://www.abc.com",reader)
req.Body = &Repeat{reader:reader,offset:0}
client.Do(req)
// Reset offset to 0
req.Body.(*Repeat).Reset()
client.Do(req)
}
This way no error will be reported. Because the Close() method is also rewritten, it also solves the problem of req.Body automatically closing when the request is reused.