在不修改请求状态的情况下读取http.Request的主体?

时间:2014-04-14 21:34:14

标签: http io go

我有一个实现http.Handler接口的类型,在其ServeHTTP方法中,检查传入的HTTP请求,执行某些操作,然后将请求转发到反向代理处理程序({ {1}})。

这样可以正常工作,只要我只检查基本的请求属性,例如URL或标题。

当我想检查传入的POST请求的正文时,例如通过调用httputil.NewSingleHostReverseProxy然后使用req.ParseForm()属性,一旦请求传递到反向代理,我就会遇到错误:

  

req.Form

我想这是因为查看HTTP请求的主体会导致http: proxy error: http: Request.ContentLength=687 with Body length 0流被耗尽,这意味着代理处理程序无法再次读取它。

我一直在玩req.Body.Readerio.Copy()之类的东西,但我并没有真正到达任何地方。

有没有办法窥视HTTP请求体(并使用内置的bufio.Peek()解析等),同时保留原始请求对象的原始状态,以便可以将其传递给反向代理?

2 个答案:

答案 0 :(得分:58)

尝试读入缓冲区,然后使用缓冲区备份两个新读者,一个供您使用,另一个供后续消费者使用。例如,假设我们要修改以下代码:

doStuff(r.Body) // r is an 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 since rdr2 implements the io.ReadCloser interface

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

请注意,*bytes.Buffer没有Close() error方法,因此它不会实现io.ReadCloser接口。因此,我们必须在*bytes.Buffer的调用中包含ioutil.NopCloser值。

答案 1 :(得分:-1)

我最近采用了不同的方法。有一种GetBody方法可用,您可以通过该方法获取请求正文的新副本,而不是执行:

doStuff(r.Body)

你可以这样做:

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

这允许您检查请求正文,同时仍然可以在以后进行进一步处理