标准库 net/http/httputil 包中 ReverseProxy 类型用于实现简单的反向代理
主要涉及到 func NewSingleHostReverseProxy(target *url.URL) *ReverseProxy 和 type ReverseProxy
ReverseProxy 结构体中重要的是Director 和 ModifyResponse 这两个函数,其中 Director用于修改请求内容,而 ModifyResponse 用于修改响应内容
NewSingleHostReverseProxy 中已经实现了 Director 函数,当然,也可以访照 NewSingleHostReverseProxy 实现自己的逻辑
一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
package main import ( "bytes" "fmt" "io/ioutil" "log" "net/http" "net/http/httputil" "net/url" "strings" ) type handle struct { host string port string } type Service struct { auth *handle user *handle } func (this *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) { var remote *url.URL // 当请求url中包含 api/auth 时 执行替换操作 if strings.Contains(r.RequestURI, "api/auth") { remote, _ = url.Parse("http://" + this.auth.host + ":" + this.auth.port) } else if strings.Contains(r.RequestURI, "api/user") { remote, _ = url.Parse("http://" + this.user.host + ":" + this.user.port) } else { fmt.Fprintf(w, "404 Not Found") return } // proxy := httputil.NewSingleHostReverseProxy(remote) // 代理的核心方法 内部实现了请求改写功能 proxy := myReverseProxy(remote) // 使用自定义ReverseProxy proxy.ServeHTTP(w, r) } // 使用自定义 ReverseProxy func myReverseProxy(target *url.URL) *httputil.ReverseProxy { targetQuery := target.RawQuery director := func(req *http.Request) { req.URL.Scheme = target.Scheme req.URL.Host = target.Host req.URL.Path, req.URL.RawPath = joinURLPath(target, req.URL) if targetQuery == "" || req.URL.RawQuery == "" { req.URL.RawQuery = targetQuery + req.URL.RawQuery } else { req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery } if _, ok := req.Header["User-Agent"]; !ok { // explicitly disable User-Agent so it's not set to default value req.Header.Set("User-Agent", "") } // 自定义header处理 删除指定header if _, ok := req.Header["Test"]; ok { req.Header.Del("Test") } // 读取并修改 request.Body // 用户输入任意帐号密码 均可登录 在反向代理过程中 修改请求体内容 if req.Body != nil && req.URL.Path == "/internal/security/login" { bodyBytes, _ := ioutil.ReadAll(req.Body) fmt.Println("读取原始请求体--->", string(bodyBytes)) // 替换请求体内容 bodyBytes = []byte(`{"username":"admin","password":"xxxxxx"}`) req.ContentLength = int64(len(bodyBytes)) // 替换后 ContentLength 会变化 需要同步修改 // 生成 req.Body req.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes)) } } return &httputil.ReverseProxy{Director: director} } // 使用自定义 ReverseProxy 工具函数 func singleJoiningSlash(a, b string) string { aslash := strings.HasSuffix(a, "/") bslash := strings.HasPrefix(b, "/") switch { case aslash && bslash: return a + b[1:] case !aslash && !bslash: return a + "/" + b } return a + b } // 使用自定义 ReverseProxy 工具函数 func joinURLPath(a, b *url.URL) (path, rawpath string) { if a.RawPath == "" && b.RawPath == "" { return singleJoiningSlash(a.Path, b.Path), "" } // Same as singleJoiningSlash, but uses EscapedPath to determine // whether a slash should be added apath := a.EscapedPath() bpath := b.EscapedPath() aslash := strings.HasSuffix(apath, "/") bslash := strings.HasPrefix(bpath, "/") switch { case aslash && bslash: return a.Path + b.Path[1:], apath + bpath[1:] case !aslash && !bslash: return a.Path + "/" + b.Path, apath + "/" + bpath } return a.Path + b.Path, apath + bpath } func startServer() { // 注册被代理的服务器 (host,port) service := &Service{ auth: &handle{host: "172.16.33.128", port: "8080"}, user: &handle{host: "127.0.0.1", port: "8082"}, } err := http.ListenAndServe(":8080", service) // http.ListenAndServe 第二个参数是 Handler interface 所以 service 需要实现该接口 提供 ServeHTTP 方法 // type Handler interface { // ServeHTTP(ResponseWriter, *Request) // } if err != nil { log.Fatalln("ListenAndServe: ", err) } } func main() { startServer() } |
参考:
1、Golang实现简单的API网关
转载请注明:轻风博客 » golang简单反向代理服务API网关