Browse Source

[feature] Add SkipClean option

- SkipClean skips cleaning routes (or a route) - e.g. when true, if the route path is "/path//to", it will remain with the double slash. This is helpful if you have a route like: /fetch/http://xkcd.com/534/
pull/162/head
Matt Silverlock 10 years ago
parent
commit
9c19ed558d
  1. 3
      .travis.yml
  2. 46
      mux.go
  3. 18
      mux_test.go
  4. 7
      route.go

3
.travis.yml

@ -10,9 +10,6 @@ matrix:
- go: 1.6 - go: 1.6
- go: tip - go: tip
install:
- go get golang.org/x/tools/cmd/vet
script: script:
- go get -t -v ./... - go get -t -v ./...
- diff -u <(echo -n) <(gofmt -d .) - diff -u <(echo -n) <(gofmt -d .)

46
mux.go

@ -48,6 +48,8 @@ type Router struct {
namedRoutes map[string]*Route namedRoutes map[string]*Route
// See Router.StrictSlash(). This defines the flag for new routes. // See Router.StrictSlash(). This defines the flag for new routes.
strictSlash bool strictSlash bool
// See Router.SkipClean(). This defines the flag for new routes.
skipClean bool
// If true, do not clear the request context after handling the request // If true, do not clear the request context after handling the request
KeepContext bool KeepContext bool
} }
@ -73,19 +75,21 @@ func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
// When there is a match, the route variables can be retrieved calling // When there is a match, the route variables can be retrieved calling
// mux.Vars(request). // mux.Vars(request).
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// Clean path to canonical form and redirect. if !r.skipClean {
if p := cleanPath(req.URL.Path); p != req.URL.Path { // Clean path to canonical form and redirect.
if p := cleanPath(req.URL.Path); p != req.URL.Path {
// Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query.
// This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue: // Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query.
// http://code.google.com/p/go/issues/detail?id=5252 // This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue:
url := *req.URL // http://code.google.com/p/go/issues/detail?id=5252
url.Path = p url := *req.URL
p = url.String() url.Path = p
p = url.String()
w.Header().Set("Location", p)
w.WriteHeader(http.StatusMovedPermanently) w.Header().Set("Location", p)
return w.WriteHeader(http.StatusMovedPermanently)
return
}
} }
var match RouteMatch var match RouteMatch
var handler http.Handler var handler http.Handler
@ -133,6 +137,19 @@ func (r *Router) StrictSlash(value bool) *Router {
return r return r
} }
// SkipClean defines the path cleaning behaviour for new routes. The initial
// value is false. Users should be careful about which routes are not cleaned
//
// When true, if the route path is "/path//to", it will remain with the double
// slash. This is helpful if you have a route like: /fetch/http://xkcd.com/534/
//
// When false, the path will be cleaned, so /fetch/http://xkcd.com/534/ will
// become /fetch/http/xkcd.com/534
func (r *Router) SkipClean(value bool) *Router {
r.skipClean = value
return r
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// parentRoute // parentRoute
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -170,7 +187,7 @@ func (r *Router) buildVars(m map[string]string) map[string]string {
// NewRoute registers an empty route. // NewRoute registers an empty route.
func (r *Router) NewRoute() *Route { func (r *Router) NewRoute() *Route {
route := &Route{parent: r, strictSlash: r.strictSlash} route := &Route{parent: r, strictSlash: r.strictSlash, skipClean: r.skipClean}
r.routes = append(r.routes, route) r.routes = append(r.routes, route)
return route return route
} }
@ -357,6 +374,7 @@ func cleanPath(p string) string {
if p[len(p)-1] == '/' && np != "/" { if p[len(p)-1] == '/' && np != "/" {
np += "/" np += "/"
} }
return np return np
} }

18
mux_test.go

@ -1386,6 +1386,24 @@ func Test301Redirect(t *testing.T) {
} }
} }
func TestSkipClean(t *testing.T) {
func1 := func(w http.ResponseWriter, r *http.Request) {}
func2 := func(w http.ResponseWriter, r *http.Request) {}
r := NewRouter()
r.SkipClean(true)
r.HandleFunc("/api/", func2).Name("func2")
r.HandleFunc("/", func1).Name("func1")
req, _ := http.NewRequest("GET", "http://localhost//api/?abc=def", nil)
res := NewRecorder()
r.ServeHTTP(res, req)
if len(res.HeaderMap["Location"]) != 0 {
t.Errorf("Shouldn't redirect since skip clean is disabled")
}
}
// https://plus.google.com/101022900381697718949/posts/eWy6DjFJ6uW // https://plus.google.com/101022900381697718949/posts/eWy6DjFJ6uW
func TestSubrouterHeader(t *testing.T) { func TestSubrouterHeader(t *testing.T) {
expected := "func1 response" expected := "func1 response"

7
route.go

@ -26,6 +26,9 @@ type Route struct {
// If true, when the path pattern is "/path/", accessing "/path" will // If true, when the path pattern is "/path/", accessing "/path" will
// redirect to the former and vice versa. // redirect to the former and vice versa.
strictSlash bool strictSlash bool
// If true, when the path pattern is "/path//to", accessing "/path//to"
// will not redirect
skipClean bool
// If true, this route never matches: it is only used to build URLs. // If true, this route never matches: it is only used to build URLs.
buildOnly bool buildOnly bool
// The name used to build URLs. // The name used to build URLs.
@ -36,6 +39,10 @@ type Route struct {
buildVarsFunc BuildVarsFunc buildVarsFunc BuildVarsFunc
} }
func (r *Route) SkipClean() bool {
return r.skipClean
}
// Match matches the route against the request. // Match matches the route against the request.
func (r *Route) Match(req *http.Request, match *RouteMatch) bool { func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
if r.buildOnly || r.err != nil { if r.buildOnly || r.err != nil {

Loading…
Cancel
Save