2014-09-19 156 views
4

我正在构建一个拦截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线,它工作正常。随着包含的行,请求返回和空主体。任何想法如何我可以得到只是http.ResponseBody,仍然写出全部响应http.ResponseWriter

+0

不应该在最后一行w.Write(响应)? – DanG 2014-09-19 15:35:33

+0

你不能只写'()''http.Response'对象(它不能转换为'[] byte')。你*可以*写(')''ResponseWriter''命名'w'。我仔细检查了这个工作,你的建议没有。 – jknupp 2014-09-19 15:39:24

+0

不理想,但你可以创建自己的结构实现io.ReadCloser把身体放回去,然后将它分配给response.Body? – DanG 2014-09-19 15:57:15

回答

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) 
} 
+0

我不确定你为什么要将头部复制回原来的区别(因为这是在原始版本中默认完成的),但你的答案确实是正确的。谢谢您的帮助。 – jknupp 2014-09-19 17:28:10

5

正如我的评论,你可以实现io.ReadCloser

按照杜伊布罗托(谢谢),你可以做到这一点用简单得多:

content, _ := ioutil.ReadAll(response.Body) 
response.Body = ioutil.NopCloser(bytes.NewReader(content)) 
response.Write(w)