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

问题描述 投票:0回答:2

我有一个实现

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
等内置解析),同时将原始请求对象保留其原始状态,以便可以将其传递给反向代理?

http io go
2个回答
89
投票

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

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

我们可以做:

buf, _ := io.ReadAll(r.Body)
rdr1 := io.NopCloser(bytes.NewBuffer(buf))
rdr2 := io.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
值包装在对
io.NopCloser
的调用中。


-2
投票

我最近使用了一种不同的方法。有一个

GetBody
方法可用,通过它你可以获取请求正文的新副本,所以不要这样做:

doStuff(r.Body)

你可以这样做:

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

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

© www.soinside.com 2019 - 2024. All rights reserved.