2015-07-21 13 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) 
} 

Reverse Proxy è funziona. Come posso ottenere il corpo della risposta?Golang: come leggere il corpo di risposta di ReverseProxy?

risposta

16

httputil.ReverseProxy ha un campo Transport. Puoi usarlo per modificare la risposta. Per esempio:

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} 

Playground esempio di tutta la faccenda: http://play.golang.org/p/b0S5CbCMrI.

+2

* small * nit, 'b' può essere spostato con' bytes.NewReader', che è una struttura un po 'più piccola. – JimB

+0

Modificato, grazie. –

3

Non conosco la soluzione migliore. Ma si può fare qualcosa di simile:

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

Questo riesce solo a stampare il proprio corpo pezzo alla volta. Dovresti comunque copiarlo in un altro 'io.Writer' per ottenere l'intero stream; per fortuna c'è già qualcosa per questo: [io.TeeReader] (http://golang.org/pkg/io/#TeeReader). (o crea un nuovo RoundTripper se vuoi che la risposta * prima * sia scritta) – JimB

+0

Sì, certo. Ho appena mostrato l'idea. Personalmente penso che la soluzione @ Ainar-G sia migliore della mia. – RoninDev

+0

Non penso che la domanda dell'OP sia abbastanza chiara da scartare questa risposta, in particolare non dice che il corpo dovrebbe essere scritto sulla stringa nel suo insieme; questo esempio, anche se un po 'troppo gonfio, mostra come catturare il flusso di risposta. Buona soluzione per ottenere subito il corpo della risposta ed eseguire, ad esempio, alcune codifiche/decodifiche. – tomasz

5

ora httputil/reverseproxy, supporto che, vedere il fonte

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 
Problemi correlati