读取http.Request的主体而不修改请求状态?

huangapple go评论76阅读模式
英文:

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

huangapple
  • 本文由 发表于 2014年4月15日 05:34:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/23070876.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定