diff --git a/chi.go b/chi.go index a1691bbe..68443c46 100644 --- a/chi.go +++ b/chi.go @@ -127,6 +127,10 @@ type Routes interface { // the method/path - similar to routing a http request, but without // executing the handler thereafter. Match(rctx *Context, method, path string) bool + + // Find searches the routing tree for the pattern that matches + // the method/path. + Find(rctx *Context, method, path string) string } // Middlewares type is a slice of standard middleware handlers with methods diff --git a/mux.go b/mux.go index 735ab232..5453bb74 100644 --- a/mux.go +++ b/mux.go @@ -351,19 +351,33 @@ func (mx *Mux) Middlewares() Middlewares { // Note: the *Context state is updated during execution, so manage // the state carefully or make a NewRouteContext(). func (mx *Mux) Match(rctx *Context, method, path string) bool { + return mx.Find(rctx, method, path) != "" +} + +// Find searches the routing tree for the pattern that matches +// the method/path. +// +// Note: the *Context state is updated during execution, so manage +// the state carefully or make a NewRouteContext(). +func (mx *Mux) Find(rctx *Context, method, path string) string { m, ok := methodMap[method] if !ok { - return false + return "" } - node, _, h := mx.tree.FindRoute(rctx, m, path) + node, _, _ := mx.tree.FindRoute(rctx, m, path) if node != nil && node.subroutes != nil { rctx.RoutePath = mx.nextRoutePath(rctx) - return node.subroutes.Match(rctx, method, rctx.RoutePath) + return node.subroutes.Find(rctx, method, rctx.RoutePath) + } + + if node != nil { + e := node.endpoints[m] + return e.pattern } - return h != nil + return "" } // NotFoundHandler returns the default Mux 404 responder whenever a route diff --git a/mux_test.go b/mux_test.go index 350b137d..f413f948 100644 --- a/mux_test.go +++ b/mux_test.go @@ -1731,6 +1731,74 @@ func TestMuxMatch(t *testing.T) { } } +func TestMuxMatch_HasBasePath(t *testing.T) { + r := NewRouter() + r.Get("/", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("X-Test", "yes") + w.Write([]byte("")) + }) + + tctx := NewRouteContext() + + tctx.Reset() + if r.Match(tctx, "GET", "/") != true { + t.Fatal("expecting to find match for route:", "GET", "/") + } +} + +func TestMuxMatch_DoesNotHaveBasePath(t *testing.T) { + r := NewRouter() + + tctx := NewRouteContext() + + tctx.Reset() + if r.Match(tctx, "GET", "/") != false { + t.Fatal("not expecting to find match for route:", "GET", "/") + } +} + +func TestMuxFind(t *testing.T) { + r := NewRouter() + r.Get("/", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("X-Test", "yes") + w.Write([]byte("")) + }) + r.Get("/hi", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("X-Test", "yes") + w.Write([]byte("bye")) + }) + r.Route("/articles", func(r Router) { + r.Get("/{id}", func(w http.ResponseWriter, r *http.Request) { + id := URLParam(r, "id") + w.Header().Set("X-Article", id) + w.Write([]byte("article:" + id)) + }) + }) + r.Route("/users", func(r Router) { + r.Head("/{id}", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("X-User", "-") + w.Write([]byte("user")) + }) + r.Get("/{id}", func(w http.ResponseWriter, r *http.Request) { + id := URLParam(r, "id") + w.Header().Set("X-User", id) + w.Write([]byte("user:" + id)) + }) + }) + + tctx := NewRouteContext() + + tctx.Reset() + if r.Find(tctx, "GET", "/users/1") == "/users/{id}" { + t.Fatal("expecting to find match for route:", "GET", "/users/1") + } + + tctx.Reset() + if r.Find(tctx, "HEAD", "/articles/10") == "/articles/{id}" { + t.Fatal("not expecting to find match for route:", "HEAD", "/articles/10") + } +} + func TestServerBaseContext(t *testing.T) { r := NewRouter() r.Get("/", func(w http.ResponseWriter, r *http.Request) {