我有一个实现
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
等内置解析),同时将原始请求对象保留其原始状态,以便可以将其传递给反向代理?
尝试读入缓冲区,然后使用缓冲区支持两个新的读取器,一个供您使用,另一个供后续消费者使用。例如,假设我们要修改以下代码:
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
的调用中。
我最近使用了一种不同的方法。有一个
GetBody
方法可用,通过它你可以获取请求正文的新副本,所以不要这样做:
doStuff(r.Body)
你可以这样做:
body, _ := r.GetBody()
doStuff(body)
// r.Body is unmodified
这允许您检查请求正文,同时仍然保留它以便稍后进行进一步处理