英文:
Reading body of http.Request without modifying request state?
问题
我有一个实现了http.Handler
接口的类型,在其ServeHTTP
方法中,会检查传入的HTTP请求,执行一些操作,然后将请求转发给一个反向代理处理程序(httputil.NewSingleHostReverseProxy
)。
这个方法在只检查基本请求属性(如URL或标头)时运行良好。
但是,当我想要检查传入的POST请求的主体时,例如通过调用req.ParseForm()
,然后使用req.Form
属性,一旦请求被传递给反向代理,就会遇到错误:
http: proxy error: http: Request.ContentLength=687 with Body length 0
我想这是因为查看HTTP请求的主体导致了req.Body.Reader
流被耗尽,这意味着代理处理程序无法再次读取它。
我尝试过使用io.Copy()
和bufio.Peek()
等方法,但没有取得实质性的进展。
有没有一种方法可以查看HTTP请求的主体(并使用req.ParseForm
等内置解析),同时保持原始请求对象的原始状态,以便将其传递给反向代理?
英文:
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
得分: 84
尝试将内容读入缓冲区,然后使用该缓冲区创建两个新的读取器,一个供您使用,另一个供后续的使用者使用。例如,假设我们想要修改以下代码:
doStuff(r.Body) // r 是一个 http.Request
我们可以这样做:
buf, _ := ioutil.ReadAll(r.Body)
rdr1 := ioutil.NopCloser(bytes.NewBuffer(buf))
rdr2 := ioutil.NopCloser(bytes.NewBuffer(buf))
doStuff(rdr1)
r.Body = rdr2 // OK,因为 rdr2 实现了 io.ReadCloser 接口
// 现在程序可以继续运行,不用关心 r.Body 是否被修改过。
请注意,*bytes.Buffer
没有 Close() error
方法,因此它不实现 io.ReadCloser
接口。因此,我们需要使用 ioutil.NopCloser
来包装我们的 *bytes.Buffer
值。
英文:
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
得分: -2
我最近对这个问题采用了不同的方法。有一个名为GetBody
的方法可用,通过它你可以获取请求体的一个新副本,所以不再需要这样做:
doStuff(r.Body)
而是可以这样做:
body, _ := r.GetBody()
doStuff(body)
// r.Body 保持不变
这样你可以在检查请求体的同时,仍然保留它以便以后进行进一步处理。
英文:
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论