2014-09-19 123 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)