2014-04-14 70 views
29

我有一個實現http.Handler接口的類型,其中在其ServeHTTP方法中檢查傳入的HTTP請求,執行一些操作,然後將請求轉發到反向代理處理程序(httputil.NewSingleHostReverseProxy)。讀取http.Request的主體而不修改請求狀態?

這工作正常,只要我只檢查基本的請求屬性,如URL或標題。

當我想檢查傳入的POST請求的正文時,例如通過調用req.ParseForm()然後使用req.Form財產,我碰到一個錯誤,一旦請求傳遞到反向代理:

http: proxy error: http: Request.ContentLength=687 with Body length 0

我想這是因爲着眼於HTTP的身體請求導致req.Body.Reader流被耗盡,這意味着代理處理程序不能再次讀取它。

我一直玩的東西,如io.Copy()bufio.Peek(),但我沒有真正得到任何地方。

有沒有辦法窺視HTTP請求體(並使用內置的解析req.ParseForm等),同時保留原始請求對象的原始狀態,以便它可以傳遞給反向代理?

+0

您是否嘗試用像'bytes.Reader'這樣的可搜索閱讀器替換'Body'?您可以將'Body'設置爲實現'ReadCloser'的每個值。 – nemo

+0

未經測試(但值得一試):根據需要製作請求的深層副本,即所有但不是Body和。*表單。然後使用io.TeeReader來克隆Request.Body,將其包裝在NopCloser中並填充到請求的深層拷貝中。將深層副本傳遞給反向代理。 – Volker

回答

49

嘗試讀入緩衝區,然後使用緩衝區來備份兩個新讀取器,一個供您使用,另一個供後續消費者使用。例如,假設我們要修改下面的代碼:

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() error方法,所以它沒有實現io.ReadCloser接口。因此,我們必須將*bytes.Buffer值包含在ioutil.NopCloser的調用中。

+0

這很好,謝謝! –

+1

是的,np!並很好地捕捉關閉簽名。我想讀文檔很重要;) – joshlf

+10

你可以使用'io.NopCloser'函數從緩衝區中獲得'io.ReadCloser',例如, 'io.NopCloser(BUF)'。請參閱http:// golang。org/src/pkg/io/ioutil/ioutil.go?s = 3623:3664#L111 –

0

我在最近使用了不同的方法。有一個GetBody可用的方法,通過它可以得到請求主體的新副本,所以不是這樣做的:

doStuff(r.Body) 

你可以代替辦:

body, _ := r.GetBody() 
doStuff(body) 
// r.Body is unmodified 

這可以讓你檢查請求正文,同時仍然有它周圍做進一步處理以後