I want to access a http.Request
's Body
multiple times. The first time happens in my authentication middleware, it uses it to recreate a sha256 signature. The second time happens later, I parse it into JSON for use in my database.
I realize that you can't read from an io.Reader
(or an io.ReadCloser
in this case) more than once. I found an answer to another question with a solution:
When you first read the body, you have to store it so once you're done with it, you can set a new
io.ReadCloser
as the request body constructed from the original data. So when you advance in the chain, the next handler can read the same body.
Then in the example they set http.Request.Body
to a new io.ReadCloser
:
// And now set a new body, which will simulate the same data we read:
r.Body = ioutil.NopCloser(bytes.NewBuffer(body))
Reading from Body
and then setting a new io.ReadCloser
at each step in my middleware seems expensive. Is this accurate?
In an effort to make this less tedious and expensive I use a solution described here to stash the parsed byte array in the Context()
value of the request. Whenever I want it, its waiting for me already as byte array:
type bodyKey int
const bodyAsBytesKey bodyKey = 0
func newContextWithParsedBody(ctx context.Context, req *http.Request) context.Context {
if req.Body == nil || req.ContentLength <= 0 {
return ctx
}
if _, ok := ctx.Value(bodyAsBytesKey).([]byte); ok {
return ctx
}
body, err := ioutil.ReadAll(req.Body)
if err != nil {
return ctx
}
return context.WithValue(ctx, bodyAsBytesKey, body)
}
func parsedBodyFromContext(ctx context.Context) []byte {
if body, ok := ctx.Value(bodyAsBytesKey).([]byte); ok {
return body
}
return nil
}
I feel like keeping a single byte array around is cheaper than reading a new one each time. Is this accurate? Are there pitfalls to this solution that I can't see?