gohttp

Read http.Response.Body multiple times


I have an *http.Response Body I want to read multiple times. I have two functions that take in an io.Reader, which in this case is the response body.

response, err := http.Get(url)
if err != nil {
    return err
}
defer func() {
    _ = response.Body.Close()
}()

// handle status code here

FirstFunction(response.Body)
SecondFunction(response.Body) // the body has been read at this point, its r/w pointer is at the end of the "file"

I'm aware of the very good io.Seeker interface, notably available on an *os.File, but response.Body doesn't implement it.

How can I read a response body twice (or more)?


Solution

  • Multiple solutions are available, but I'm in favor of using io.Seeker, as this interface allows for a single reader to be usable multiple times.

    Here's my preferred solution:

    response, err := http.Get(url)
    if err != nil {
        return err
    }
    defer func() {
        _ = response.Body.Close()
    }()
    
    // handle status code here
    
    wholeBody, err := io.ReadAll(response.Body)
    if err != nil {
        return err
    }
    bytesReader := bytes.NewReader(wholeBody)
    
    FirstFunction(bytesReader)
    
    _, err = bytesReader.Seek(0, io.SeekStart) // *bytes.Reader implements io.Seeker
    if err != nil {
        return err
    }
    
    SecondFunction(bytesReader) // we finally have the full body again!
    

    Honorable mention also to https://github.com/jfbus/httprs, discovered while seeking for an answer to this problem, which could be an out-of-the-box fit for some people.