我正在构建一个拦截HTTP请求的简单缓存代理,在response.Body中抓取内容,然后将其写回客户端。问题是,只要我从response.Body读取,写回客户端就包含一个空体(其他所有内容,如标题,都按预期编写)。
这是当前的代码:
func requestHandler(w http.ResponseWriter, r *http.Request) {
client := &http.Client{}
r.RequestURI = ""
response, err := client.Do(r)
defer response.Body.Close()
if err != nil {
log.Fatal(err)
}
content, _ := ioutil.ReadAll(response.Body)
cachePage(response.Request.URL.String(), content)
response.Write(w)
}
如果删除content, _
和cachePage
行,则可以正常使用。包含行,请求返回和空体。我知道如何只是 Body
的{{1}}并仍然完整地将回复写到http.Response
?
答案 0 :(得分:5)
在我的评论中,你可以实现io.ReadCloser
根据Dewy Broto(谢谢)你可以通过以下方式做到这一点:
content, _ := ioutil.ReadAll(response.Body)
response.Body = ioutil.NopCloser(bytes.NewReader(content))
response.Write(w)
答案 1 :(得分:2)
正如您所发现的,您只能从请求的正文中读取一次。
Go有一个反向代理,可以帮助您尝试做什么。查看httputil.ReverseProxy和httputil.DumpResponse
答案 2 :(得分:1)
您无需再次阅读回复。您已掌握了数据并可以直接将其写入响应编写器。
电话
response.Write(w)
将有线格式的响应写入服务器的响应正文。这不是您想要的代理。您需要单独将标题,状态和正文复制到服务器响应。
我在下面的代码评论中注意到了其他问题。
我建议使用标准库ReverseProxy或复制它并修改它以满足您的需求。
func requestHandler(w http.ResponseWriter, r *http.Request) {
// No need to make a client, use the default
// client := &http.Client{}
r.RequestURI = ""
response, err := http.DefaultClient.Do(r)
// response can be nil, close after error check
// defer response.Body.Close()
if err != nil {
log.Fatal(err)
}
defer response.Body.Close()
// Check errors! Always.
// content, _ := ioutil.ReadAll(response.Body)
content, err := ioutil.ReadAll(response.Body)
if err != nil {
// handle error
}
cachePage(response.Request.URL.String(), content)
// The Write method writes the response in wire format to w.
// Because the server handles the wire format, you need to do
// copy the individual pieces.
// response.Write(w)
// Copy headers
for k, v := range response.Header {
w.Header()[k] = v
}
// Copy status code
w.WriteHeader(response.StatusCode)
// Write the response body.
w.Write(content)
}