【问题标题】:Reading body of http.Request without modifying request state?在不修改请求状态的情况下读取 http.Request 的正文?
【发布时间】:2014-04-14 21:34:13
【问题描述】:

我有一个实现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等的内置解析),同时将原始请求对象保持在原始状态,以便可以将其传递给反向代理?

【问题讨论】:

  • 您是否尝试过将Body 替换为像bytes.Reader 这样的可搜索阅读器?您可以将Body 设置为实现ReadCloser 的每个值。
  • 未经测试(但值得一试):根据需要制作请求的深层副本,即除了正文和 .*Form 之外的所有内容。然后使用 io.TeeReader 克隆 Request.Body,将其包装在 NopCloser 中并填充到请求的深层副本中。将深层副本传递给反向代理。

标签: http io go


【解决方案1】:

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

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 的调用中。

【讨论】:

【解决方案2】:

我最近使用了一种不同的方法来解决这个问题。有一个GetBody 方法可用,通过它您可以获得请求正文的新副本,所以不要这样做:

doStuff(r.Body)

你可以这样做:

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

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

【讨论】:

  • GetBody() 仅用于客户端请求。对于服务器请求,它是未使用的。
猜你喜欢
  • 2021-06-16
  • 1970-01-01
  • 2017-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-20
相关资源
最近更新 更多