在不修改请求状态的情况下读取http.Request的正文?
我有一个实现在不修改请求状态的情况下读取http.Request的正文?,http,io,go,Http,Io,Go,我有一个实现http.Handler接口的类型,在它的ServeHTTP方法中,检查传入的http请求,采取一些操作,然后将请求转发到反向代理处理程序(httputil.newsinglehostreverseeproxy) 只要我只检查基本的请求属性,比如URL或头,这就行了 当我想检查传入POST请求的主体时,例如,通过调用req.ParseForm()然后使用req.Form属性,当请求传递到反向代理时,我会遇到一个错误: http:proxy错误:http:Request.Content
http.Handler
接口的类型,在它的ServeHTTP
方法中,检查传入的http请求,采取一些操作,然后将请求转发到反向代理处理程序(httputil.newsinglehostreverseeproxy
)
只要我只检查基本的请求属性,比如URL或头,这就行了
当我想检查传入POST请求的主体时,例如,通过调用req.ParseForm()
然后使用req.Form
属性,当请求传递到反向代理时,我会遇到一个错误:
http:proxy错误:http:Request.ContentLength=687,正文长度为0
我认为这是因为查看HTTP请求的主体会导致req.body.Reader
流被耗尽,这意味着代理处理程序无法再次读取它
我一直在玩io.Copy()
和bufio.Peek()
之类的游戏,但我并没有真正取得任何进展
有没有一种方法可以窥视HTTP请求主体(并使用
req.ParseForm
等的内置解析),同时将原始请求对象保持在其原始状态,以便将其传递给反向代理?尝试读取缓冲区,然后使用缓冲区备份两个新读卡器,其中一个供您使用,一个供后续消费者使用。例如,假设我们要修改以下代码:
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()错误
方法,因此它没有实现io.ReadCloser
接口。因此,我们必须在调用ioutil.nocloser
时包装*bytes.Buffer
值。最近我使用了不同的方法来处理这个问题。有一个可用的GetBody
方法,您可以通过该方法获取请求正文的新副本,因此不必执行以下操作:
doStuff(r.Body)
你可以改为:
body, _ := r.GetBody()
doStuff(body)
// r.Body is unmodified
这允许您在检查请求正文的同时仍保留它,以便稍后进行进一步处理您是否尝试过用可查找的读取器(如
字节读取器)替换正文
?您可以将Body
设置为实现ReadCloser
的每个值。未测试(但值得一试):根据需要制作请求的深度副本,即除了Body和。*表单之外的所有内容。然后使用io.teereder克隆Request.Body,将其包装在NoCloser中,并填充到请求的deepcopy中。将深度副本传递给反向代理。是的,np!很好地捕捉到了密切的签名。我想阅读文档毕竟很重要;)您可以使用io.nocloser
功能从缓冲区获取io.ReadCloser
,例如io.nocloser(buf)
。请看,TeeReader可能会很有用。这很有效,但仅适用于可以保存在内存或磁盘中的绑定实体。drainBody
in提供了类似的解决方案,但考虑了http。没有人将其考虑在内。GetBody()
仅用于客户端请求。对于服务器请求,它是未使用的。