Reading body of http.Request without modifying req

2019-01-16 13:17发布

问题:

I have a type implementing the http.Handler interface where, in its ServeHTTP method, incoming HTTP requests are inspected, some action is taken, and then the requests are forwarded to a reverse proxy handler (httputil.NewSingleHostReverseProxy).

This works fine, so long as I'm only inspecting the basic request properties, such as the URL or headers.

When I want to inspect the body of an incoming POST request, e.g. by calling req.ParseForm() and then using the req.Form property, I run into an error once the request is passed onto the reverse proxy:

http: proxy error: http: Request.ContentLength=687 with Body length 0

I imagine this happens because looking at the body of the HTTP request causes the req.Body.Reader stream to be drained, meaning it cannot be read again by the proxy handler.

I've been playing with things like io.Copy() and bufio.Peek(), but I'm not really getting anywhere.

Is there a way to peek at the HTTP request body (and use the built-in parsing of req.ParseForm etc.), while leaving the original request object in its original state, so that it can be passed to the reverse proxy?

回答1:

Try reading into a buffer and then using the buffer to back two new readers, one for you to use, and one for subsequent consumers to use. For example, imagine that we want to modify the following code:

doStuff(r.Body) // r is an http.Request

We could do:

buf, _ := ioutil.ReadAll(r.Body)
rdr1 := ioutil.NopCloser(bytes.NewBuffer(buf))
rdr2 := ioutil.NopCloser(bytes.NewBuffer(buf))

doStuff(rdr1)
r.Body = rdr2 // OK since rdr2 implements the io.ReadCloser interface

// Now the program can continue oblivious to the fact that
// r.Body was ever touched.

Note that *bytes.Buffer does not have a Close() error method, so it doesn't implement the io.ReadCloser interface. Thus, we have to wrap our *bytes.Buffer values in calls to ioutil.NopCloser.



回答2:

I used a different approach to this more recently. There is a GetBody method available, through which you can get a new copy of the request body, so instead of doing:

doStuff(r.Body)

You could instead do:

body, _ := r.GetBody()
doStuff(body)
// r.Body is unmodified

This allows you to inspect the request body while still having it around to do further processing later



标签: http io go