diff --git a/mux_test.go b/mux_test.go index 8ad57ac8..5198024d 100644 --- a/mux_test.go +++ b/mux_test.go @@ -2715,6 +2715,38 @@ func Test_copyRouteConf(t *testing.T) { } } +func TestMethodNotAllowed(t *testing.T) { + handler := func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) } + router := NewRouter() + router.HandleFunc("/thing", handler).Methods(http.MethodGet) + router.HandleFunc("/something", handler).Methods(http.MethodGet) + + w := NewRecorder() + req := newRequest(http.MethodPut, "/thing") + + router.ServeHTTP(w, req) + + if w.Code != 405 { + t.Fatalf("Expected status code 405 (got %d)", w.Code) + } +} + +func TestSubrouterNotFound(t *testing.T) { + handler := func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) } + router := NewRouter() + router.Path("/a").Subrouter().HandleFunc("/thing", handler).Methods(http.MethodGet) + router.Path("/b").Subrouter().HandleFunc("/something", handler).Methods(http.MethodGet) + + w := NewRecorder() + req := newRequest(http.MethodPut, "/not-present") + + router.ServeHTTP(w, req) + + if w.Code != 404 { + t.Fatalf("Expected status code 404 (got %d)", w.Code) + } +} + // mapToPairs converts a string map to a slice of string pairs func mapToPairs(m map[string]string) []string { var i int diff --git a/route.go b/route.go index a1970966..16a7cdf4 100644 --- a/route.go +++ b/route.go @@ -43,11 +43,6 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool { return false } - // Set MatchErr to nil to prevent - // subsequent matching subrouters from failing to run middleware. - // If not reset, the middleware would see a non-nil MatchErr and be skipped, - // even when there was a matching route. - match.MatchErr = nil var matchErr error // Match everything. @@ -57,6 +52,18 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool { matchErr = ErrMethodMismatch continue } + + // Ignore ErrNotFound errors. These errors arise from match call + // to Subrouters. + // + // This prevents subsequent matching subrouters from failing to + // run middleware. If not ignored, the middleware would see a + // non-nil MatchErr and be skipped, even when there was a + // matching route. + if match.MatchErr == ErrNotFound { + match.MatchErr = nil + } + matchErr = nil return false }