2015-07-21 194 views
10
package main 

import (
    "net/http" 
    "net/http/httputil" 
    "net/url" 
) 

func main() { 
    target := &url.URL{Scheme: "http", Host: "www.google.com"} 
    proxy := httputil.NewSingleHostReverseProxy(target) 

    http.Handle("/google", proxy) 
    http.ListenAndServe(":8099", nil) 
} 

反向代理是有效的。我如何獲得響應主體?Golang:如何閱讀ReverseProxy的響應體?

回答

16

httputil.ReverseProxyTransport場。您可以使用它來修改響應。例如:

type transport struct { 
    http.RoundTripper 
} 

func (t *transport) RoundTrip(req *http.Request) (resp *http.Response, err error) { 
    resp, err = t.RoundTripper.RoundTrip(req) 
    if err != nil { 
     return nil, err 
    } 
    b, err := ioutil.ReadAll(resp.Body) 
    if err != nil { 
     return nil, err 
    } 
    err = resp.Body.Close() 
    if err != nil { 
     return nil, err 
    } 
    b = bytes.Replace(b, []byte("server"), []byte("schmerver"), -1) 
    body := ioutil.NopCloser(bytes.NewReader(b)) 
    resp.Body = body 
    resp.ContentLength = int64(len(b)) 
    resp.Header.Set("Content-Length", strconv.Itoa(len(b))) 
    return resp, nil 
} 

// ... 
proxy := httputil.NewSingleHostReverseProxy(target) 
proxy.Transport = &transport{http.DefaultTransport} 

整個事情的遊樂場示例:http://play.golang.org/p/b0S5CbCMrI

+2

* small * nit,'b'可以用'bytes.NewReader'包裝,這是一個小一點的結構。 – JimB

+0

編輯,謝謝。 –

3

我不知道最佳解決方案。但是你可以做這樣的事情:

package main 

import (
    "fmt" 
    "net/http" 
    "net/http/httputil" 
    "net/url" 
) 

func main() { 
    target := &url.URL{Scheme: "http", Host: "www.google.com"} 
    proxy := httputil.NewSingleHostReverseProxy(target) 

    http.Handle("/google", CustomHandler(proxy)) 
    http.ListenAndServe(":8099", nil) 
} 

func CustomHandler(h http.Handler) http.HandlerFunc { 
    return func(res http.ResponseWriter, req *http.Request) { 
     h.ServeHTTP(NewCustomWriter(res), req) 
    } 
} 

type customWriter struct { 
    http.ResponseWriter 
} 

func NewCustomWriter(w http.ResponseWriter) *customWriter { 
    return &customWriter{w} 
} 

func (c *customWriter) Header() http.Header { 
    return c.ResponseWriter.Header() 
} 

func (c *customWriter) Write(data []byte) (int, error) { 
    fmt.Println(string(data)) //get response here 
    return c.ResponseWriter.Write(data) 
} 

func (c *customWriter) WriteHeader(i int) { 
    c.ResponseWriter.WriteHeader(i) 
} 
+0

這隻能管理在同一時間打印出身體一個大塊。你仍然需要將它複製到另一個'io.Writer'中以獲取整個流;幸運的是,已經有這樣的東西:[io.TeeReader](http://golang.org/pkg/io/#TeeReader)。 (或者如果您希望在寫出之前回復*,請創建一個新的RoundTripper) – JimB

+0

當然可以。我只是展示了這個想法。我個人認爲@Ainar-G解決方案比我的更好。 – RoninDev

+0

我不認爲OP的問題已經足夠清楚了,可以放棄這個答案,特別是它並沒有說身體應該被寫入整個字符串;這個例子雖然有點過於臃腫,但卻展示瞭如何捕獲響應流。用於即時獲取響應主體並執行(例如)某些編碼/解碼的良好解決方案。 – tomasz

5

現在httputil /反向代理,支持比,看到源

type ReverseProxy struct { 
     ... 

     // ModifyResponse is an optional function that 
     // modifies the Response from the backend 
     // If it returns an error, the proxy returns a StatusBadGateway error. 
     ModifyResponse func(*http.Response) error 
    } 



func rewriteBody(resp *http.Response) (err error) { 
    b, err := ioutil.ReadAll(resp.Body) //Read html 
    if err != nil { 
     return err 
    } 
    err = resp.Body.Close() 
    if err != nil { 
     return err 
    } 
    b = bytes.Replace(b, []byte("server"), []byte("schmerver"), -1) // replace html 
    body := ioutil.NopCloser(bytes.NewReader(b)) 
    resp.Body = body 
    resp.ContentLength = int64(len(b)) 
    resp.Header.Set("Content-Length", strconv.Itoa(len(b))) 
    return nil 
} 

// ... 
target, _ := url.Parse("http://example.com") 
proxy := httputil.NewSingleHostReverseProxy(target) 
proxy.ModifyResponse = rewriteBody