diff --git a/mux.go b/mux.go index 711630d..e22dcac 100644 --- a/mux.go +++ b/mux.go @@ -218,6 +218,12 @@ func (r *Router) Schemes(schemes ...string) *Route { return r.NewRoute().Schemes(schemes...) } +// BuildVars registers a new route with a custom function for modifying +// route variables before building a URL. +func (r *Router) BuildVarsFunc(f BuildVarsFunc) *Route { + return r.NewRoute().BuildVarsFunc(f) +} + // ---------------------------------------------------------------------------- // Context // ---------------------------------------------------------------------------- diff --git a/mux_test.go b/mux_test.go index 292776d..bd76a01 100644 --- a/mux_test.go +++ b/mux_test.go @@ -511,6 +511,25 @@ func TestMatcherFunc(t *testing.T) { } } +func TestBuildVarsFunc(t *testing.T) { + tests := []routeTest{ + { + route: new(Route).Path(`/111/{v1:\d}{v2:.*}`).BuildVarsFunc(func(vars map[string]string) map[string]string { + vars["v1"] = "3" + vars["v2"] = "a" + return vars + }), + request: newRequest("GET", "http://localhost/111/2"), + path: "/111/3a", + shouldMatch: true, + }, + } + + for _, test := range tests { + testRoute(t, test) + } +} + func TestSubRouter(t *testing.T) { subrouter1 := new(Route).Host("{v1:[a-z]+}.google.com").Subrouter() subrouter2 := new(Route).PathPrefix("/foo/{v1}").Subrouter() diff --git a/regexp.go b/regexp.go index 4c3482b..e56f1bb 100644 --- a/regexp.go +++ b/regexp.go @@ -133,11 +133,7 @@ func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool { } // url builds a URL part using the given values. -func (r *routeRegexp) url(pairs ...string) (string, error) { - values, err := mapFromPairs(pairs...) - if err != nil { - return "", err - } +func (r *routeRegexp) url(values map[string]string) (string, error) { urlValues := make([]interface{}, len(r.varsN)) for k, v := range r.varsN { value, ok := values[v] diff --git a/route.go b/route.go index 7766254..e79afa7 100644 --- a/route.go +++ b/route.go @@ -31,6 +31,8 @@ type Route struct { name string // Error resulted from building a route. err error + + buildVarsFunc BuildVarsFunc } // Match matches the route against the request. @@ -336,6 +338,19 @@ func (r *Route) Schemes(schemes ...string) *Route { return r.addMatcher(schemeMatcher(schemes)) } +// BuildVarsFunc -------------------------------------------------------------- + +// BuildVarsFunc is the function signature used by custom build variable +// functions (which can modify route variables before a route's URL is built). +type BuildVarsFunc func(map[string]string) map[string]string + +// BuildVarsFunc adds a custom function to be used to modify build variables +// before a route's URL is built. +func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route { + r.buildVarsFunc = f + return r +} + // Subrouter ------------------------------------------------------------------ // Subrouter creates a subrouter for the route. @@ -398,17 +413,20 @@ func (r *Route) URL(pairs ...string) (*url.URL, error) { if r.regexp == nil { return nil, errors.New("mux: route doesn't have a host or path") } + values, err := r.buildVars(pairs...) + if err != nil { + return nil, err + } var scheme, host, path string - var err error if r.regexp.host != nil { // Set a default scheme. scheme = "http" - if host, err = r.regexp.host.url(pairs...); err != nil { + if host, err = r.regexp.host.url(values); err != nil { return nil, err } } if r.regexp.path != nil { - if path, err = r.regexp.path.url(pairs...); err != nil { + if path, err = r.regexp.path.url(values); err != nil { return nil, err } } @@ -429,7 +447,11 @@ func (r *Route) URLHost(pairs ...string) (*url.URL, error) { if r.regexp == nil || r.regexp.host == nil { return nil, errors.New("mux: route doesn't have a host") } - host, err := r.regexp.host.url(pairs...) + values, err := r.buildVars(pairs...) + if err != nil { + return nil, err + } + host, err := r.regexp.host.url(values) if err != nil { return nil, err } @@ -449,7 +471,11 @@ func (r *Route) URLPath(pairs ...string) (*url.URL, error) { if r.regexp == nil || r.regexp.path == nil { return nil, errors.New("mux: route doesn't have a path") } - path, err := r.regexp.path.url(pairs...) + values, err := r.buildVars(pairs...) + if err != nil { + return nil, err + } + path, err := r.regexp.path.url(values) if err != nil { return nil, err } @@ -458,6 +484,19 @@ func (r *Route) URLPath(pairs ...string) (*url.URL, error) { }, nil } +// buildVars converts the route variable pairs into a map. If the route has a +// BuildVarsFunc, it is invoked. +func (r *Route) buildVars(pairs ...string) (map[string]string, error) { + m, err := mapFromPairs(pairs...) + if err != nil { + return nil, err + } + if r.buildVarsFunc != nil { + m = r.buildVarsFunc(m) + } + return m, nil +} + // ---------------------------------------------------------------------------- // parentRoute // ----------------------------------------------------------------------------