多次从阅读器中读取

时间:2014-09-19 15:26:00

标签: io go

我正在构建一个拦截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

3 个答案:

答案 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.ReverseProxyhttputil.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)
}