我正在尝试将gorilla mux和httputil.ReverseProxy一起使用,但在尝试获取mux.Vars时它是空的。根据{{3}},似乎http.Request指针是原始请求的浅表副本,它仍应有效。
有什么想法吗?
https://golang.org/src/net/http/httputil/reverseproxy.go?s=2744:2819#L93
package main
import (
"github.com/gorilla/mux"
"log"
"net/http"
"net/http/httputil"
"net/url"
)
type route struct {
match string
base string
}
var routes = []route{
// proxy http://localhost:3000/api/foo/bar => https://api.bar.com/5/foo/bar
route{match: "/api/{path}", base: "https://api.bar.com/5"},
route{match: "/sales/{path}", base: "https://sales.bar.com/3"},
}
func NewProxy(r *route) http.Handler {
director := func(req *http.Request) {
out, _ := url.Parse(r.base)
req.URL.Scheme = out.Scheme
req.URL.Host = out.Host
req.URL.Path = out.Path + "/" + mux.Vars(req)["path"] // mux Vars are empty here
}
return &httputil.ReverseProxy{Director: director}
}
func main() {
for _, route := range routes {
http.Handle(route.match, NewProxy(&route))
}
log.Println("Listening on port 8080")
http.ListenAndServe(":8080", nil)
}
答案 0 :(得分:5)
这里有两个不同的问题。
第一个,您没有使用mux.Router
,因此gorilla/mux
没有机会预处理您的请求。换句话说,请求直接从http
包发送到您的反向代理。这个问题很容易解决:
r := mux.NewRouter()
for _, route := range routes {
r.Handle(route.match, NewProxy(&route))
}
http.Handle("/", r)
第二个问题比第一个问题更棘手。此问题与mux
包的实现方式有关。如果你看mux.Vars()
实现,你会看到它使用了一个名为Context
的东西。如official documentation中所述,Context
是存储请求生命周期内共享的值的东西。简化的上下文实现将是:
type Context map[*http.Request]interface{}
func (c Context) Set(req *http.Request, v interface{}) {
c[req] = v
}
func (c Context) Get(req *http.Request) interface{} {
return c[req]
}
如您所见,给定http.Request
,我们可以在上下文中存储值。之后,我们可以使用相同的Context
和相同的http.Request
检索这些值。 mux
使用全局Context
来存储在路由过程中解析的变量,以便您可以使用标准http.request
。但是,由于httputil.ReverseProxy
会按要求传递实际请求和Context
链接值的副本,因此新Request
在Context
中没有值。
要解决此问题,您可以根据ReverseProxy
:
httputil.ReverseProxy
type MyReverseProxy struct {
httputil.ReverseProxy
Director func(inr, outr *http.Request)
}
func (p *MyReverseProxy) ServeHTTP(rw http.ResponseWriter, inr *http.Request) {
p.ReverseProxy.Director = func(outr *http.Request) {
p.Director(inr, outr)
}
p.ReverseProxy.ServeHTTP(rw, inr)
}
func NewProxy(r *route) http.Handler {
director := func(inr, outr *http.Request) {
out, _ := url.Parse(r.base)
outr.URL.Scheme = out.Scheme
outr.URL.Host = out.Host
outr.URL.Path = out.Path + "/" + mux.Vars(inr)["path"]
log.Printf("IN VARS: %#v\n", mux.Vars(inr)) // Now inr has proper vars
log.Printf("OUT VARS: %#v\n", mux.Vars(outr))
}
return &MyReverseProxy{Director: director}
您甚至可以使用context
并保留Director
声明:
type MyReverseProxy struct {
httputil.ReverseProxy
Director func(req *http.Request)
}
func (p *MyReverseProxy) ServeHTTP(rw http.ResponseWriter, inr *http.Request) {
p.ReverseProxy.Director = func(outr *http.Request) {
context.Set(outr, "in_req", inr)
p.Director(outr)
}
p.ReverseProxy.ServeHTTP(rw, inr)
}
func NewProxy(r *route) http.Handler {
director := func(outr *http.Request) {
out, _ := url.Parse(r.base)
inr := context.Get(outr, "in_req").(*http.Request)
outr.URL.Scheme = out.Scheme
outr.URL.Host = out.Host
outr.URL.Path = out.Path + "/" + mux.Vars(inr)["path"]
log.Printf("IN VARS: %#v\n", mux.Vars(inr)) // Now inr has proper vars
log.Printf("OUT VARS: %#v\n", mux.Vars(outr))
}
return &MyReverseProxy{Director: director}
}
这两种实现对我来说都很棘手。他们必须在每次通话中更改httputil.ReverseProxy
的{{1}}。所以,我可能会接受Director
在这里不是一个好选择,而是我会使用一些更简单的解决方案:
mux
您可以阅读mux
source code以实现基于正则表达式的复杂解决方案。