如何读取ReverseProxy的响应体?

huangapple go评论86阅读模式
英文:

How to read response body of ReverseProxy

问题

以下是翻译好的代码:

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)
}

反向代理已经工作了。我如何获取响应体?

英文:
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 is works. How can I get the response body?

答案1

得分: 33

现在httputil/reverseproxy支持修改响应的功能。你可以看一下源代码。

type ReverseProxy struct {
    ...
    
    // ModifyResponse是一个可选的函数,用于修改来自后端的响应。
    // 如果它返回一个错误,代理将返回StatusBadGateway错误。
    ModifyResponse func(*http.Response) error
}

func rewriteBody(resp *http.Response) (err error) {
    b, err := ioutil.ReadAll(resp.Body) //读取html
    if err != nil {
        return err
    }
    err = resp.Body.Close()
    if err != nil {
        return err
    }
    b = bytes.Replace(b, []byte("server"), []byte("schmerver"), -1) //替换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

以上是要翻译的内容。

英文:

now httputil/reverseproxy, support than, see source

 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

答案2

得分: 31

httputil.ReverseProxy有一个Transport字段,你可以使用它来修改响应。例如:

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 链接:http://play.golang.org/p/b0S5CbCMrI。

英文:

httputil.ReverseProxy has a Transport field. You can use it to modify the response. For example:

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 example of the whole thing: http://play.golang.org/p/b0S5CbCMrI.

答案3

得分: 5

我不知道最好的解决方案。但你可以尝试像这样做:

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)) // 在这里获取响应
	return c.ResponseWriter.Write(data)
}

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

希望对你有帮助!

英文:

I don't know best solution. But you can do something like this:

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)
}

答案4

得分: -1

从源代码中,httptest.ResponseRecorder 用于从处理程序获取响应。

func TestModifyResponseClosesBody(t *testing.T) {
	req, _ := http.NewRequest("GET", "http://foo.tld/", nil)
	req.RemoteAddr = "1.2.3.4:56789"
	closeCheck := new(checkCloser)
	logBuf := new(bytes.Buffer)
	outErr := errors.New("ModifyResponse error")
	rp := &ReverseProxy{
		Director: func(req *http.Request) {},
		Transport: &staticTransport{&http.Response{
			StatusCode: 200,
			Body:       closeCheck,
		}},
		ErrorLog: log.New(logBuf, "", 0),
		ModifyResponse: func(*http.Response) error {
			return outErr
		},
	}
	rec := httptest.NewRecorder()
	rp.ServeHTTP(rec, req)
	res := rec.Result()
	if g, e := res.StatusCode, http.StatusBadGateway; g != e {
		t.Errorf("got res.StatusCode %d; expected %d", g, e)
	}
	if !closeCheck.closed {
		t.Errorf("body should have been closed")
	}
	if g, e := logBuf.String(), outErr.Error(); !strings.Contains(g, e) {
		t.Errorf("ErrorLog %q does not contain %q", g, e)
	}
}
英文:

From source code httptest.ResponseRecorder is use for get the response from the handler

func TestModifyResponseClosesBody(t *testing.T) {
	req, _ := http.NewRequest("GET", "http://foo.tld/", nil)
	req.RemoteAddr = "1.2.3.4:56789"
	closeCheck := new(checkCloser)
	logBuf := new(bytes.Buffer)
	outErr := errors.New("ModifyResponse error")
	rp := &ReverseProxy{
		Director: func(req *http.Request) {},
		Transport: &staticTransport{&http.Response{
			StatusCode: 200,
			Body:       closeCheck,
		}},
		ErrorLog: log.New(logBuf, "", 0),
		ModifyResponse: func(*http.Response) error {
			return outErr
		},
	}
	rec := httptest.NewRecorder()
	rp.ServeHTTP(rec, req)
	res := rec.Result()
	if g, e := res.StatusCode, http.StatusBadGateway; g != e {
		t.Errorf("got res.StatusCode %d; expected %d", g, e)
	}
	if !closeCheck.closed {
		t.Errorf("body should have been closed")
	}
	if g, e := logBuf.String(), outErr.Error(); !strings.Contains(g, e) {
		t.Errorf("ErrorLog %q does not contain %q", g, e)
	}
}

huangapple
  • 本文由 发表于 2015年7月21日 17:43:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/31535569.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定