Repository: gorilla/mux (go)

Issue:
[BUG] MethodNotAllowedHandler does not work for subrouter with different routes
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Current Behavior

> Go version: 1.20
    Mux version: 1.8.1

Starting with mux `v1.8.1` we encountered the following problem, which was not present in previous versions of the library.
If there are several routes in one subrouter, for example `/api` and `/api/{id}` MethodNotAllowedHandler does not work and NotFoundHandler is used by default.

An example test to reproduce the bug is shown below. The bug is not reproduced if:
- no subrouter is used, just main mux.Router
- subrouter contains only routes of the same type (only `/api` for example)
- place HandleFunc for `/api/{id}` before HandleFunc for `/api` in the code

After examining the modified code, it was found out that the problem is related to the addition of this else condition:
https://github.com/gorilla/mux/blob/v1.8.1/route.go#L70-L78
Since now the result of the Route.Match function depends on which Route was processed last (the value of match.MatchErr by reference is updated).

### Expected Behavior

Result 405 Method Not Allowed

### Steps To Reproduce

1. Run test:
```
func TestMiddlewareMethodMismatch(t *testing.T) {
	router := mux.NewRouter()
	router.MethodNotAllowedHandler = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		rw.WriteHeader(http.StatusMethodNotAllowed)
		_, _ = rw.Write([]byte("Method not allowed"))
	})

	subrouter := router.PathPrefix("/v1").Subrouter()
	subrouter.HandleFunc("/api", func(w http.ResponseWriter, e *http.Request) {
		_, _ = w.Write([]byte("Logic"))
	}).Methods("GET")
	subrouter.HandleFunc("/api/{id}", func(w http.ResponseWriter, e *http.Request) {
		_, _ = w.Write([]byte("Logic"))
	}).Methods("GET")

	rw := httptest.NewRecorder()
	req, _ := http.NewRequest("POST", "/v1/api", nil)
	router.ServeHTTP(rw, req)

	if rw.Code == http.StatusNotFound {
		t.Fatal("Not found instead of Method Not Allowed")
	}
}
```

### Anything else?

_No response_

Pull request description:
[BUG #739] Fixed handling of invalid query params
## What type of PR is this? (check all applicable)

- [ ] Refactor
- [ ] Feature
- [x] Bug Fix
- [ ] Optimization
- [ ] Documentation Update
- [ ] Go Version Update
- [ ] Dependency Update

## Description
Fix made in #712 is not an optimal solution to the problem #704, since it completely prevents the possibility of getting error `ErrMethodMismatch` for subrouters.

The proposed solution corrects this situation and allows correct error handling of invalid query paramaters and preserves the possibility of getting `ErrMethodMismatch` in subrouters.

## Related Tickets & Documents

- Related Issue #739
- Closes #739

## Added/updated tests?

- [x] Yes
- [ ] No, and this is why: _please replace this line with details on why tests
      have not been included_
- [ ] I need help with writing tests

## Run verifications and test

- [x] `make verify` is passing
- [x] `make test` is passing

Patch:
diff --git a/route.go b/route.go
index 8a9e754..b6582da 100644
--- a/route.go
+++ b/route.go
@@ -53,6 +53,19 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
 				continue
 			}
 
+			// Multiple routes may share the same path but use different HTTP methods. For instance:
+			// Route 1: POST "/users/{id}".
+			// Route 2: GET "/users/{id}", parameters: "id": "[0-9]+".
+			//
+			// The router must handle these cases correctly. For a GET request to "/users/abc" with "id" as "-2",
+			// The router should return a "Not Found" error as no route fully matches this request.
+			if rr, ok := m.(*routeRegexp); ok {
+				if rr.regexpType == regexpTypeQuery {
+					matchErr = ErrNotFound
+					break
+				}
+			}
+
 			// Ignore ErrNotFound errors. These errors arise from match call
 			// to Subrouters.
 			//
@@ -66,16 +79,6 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
 
 			matchErr = nil // nolint:ineffassign
 			return false
-		} else {
-			// Multiple routes may share the same path but use different HTTP methods. For instance:
-			// Route 1: POST "/users/{id}".
-			// Route 2: GET "/users/{id}", parameters: "id": "[0-9]+".
-			//
-			// The router must handle these cases correctly. For a GET request to "/users/abc" with "id" as "-2",
-			// The router should return a "Not Found" error as no route fully matches this request.
-			if match.MatchErr == ErrMethodMismatch {
-				match.MatchErr = nil
-			}
 		}
 	}
 
@@ -84,7 +87,7 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
 		return false
 	}
 
-	if match.MatchErr == ErrMethodMismatch && r.handler != nil {
+	if match.MatchErr != nil && r.handler != nil {
 		// We found a route which matches request method, clear MatchErr
 		match.MatchErr = nil
 		// Then override the mis-matched handler


Test patch:
diff --git a/mux_test.go b/mux_test.go
index cb3bbe0..0845d7f 100644
--- a/mux_test.go
+++ b/mux_test.go
@@ -2116,6 +2116,24 @@ func TestMultipleDefinitionOfSamePathWithDifferentMethods(t *testing.T) {
 
 }
 
+func TestMultipleDefinitionOfSamePathWithDifferentQueries(t *testing.T) {
+	emptyHandler := func(w http.ResponseWriter, r *http.Request) {}
+
+	r := NewRouter()
+	r.HandleFunc("/api", emptyHandler).Queries("foo", "{foo:[0-9]+}").Methods(http.MethodGet)
+	r.HandleFunc("/api", emptyHandler).Queries("bar", "{bar:[0-9]+}").Methods(http.MethodGet)
+
+	req := newRequest(http.MethodGet, "/api?bar=4")
+	match := new(RouteMatch)
+	matched := r.Match(req, match)
+	if !matched {
+		t.Error("Should have matched route for methods")
+	}
+	if match.MatchErr != nil {
+		t.Error("Should have no error. Found:", match.MatchErr)
+	}
+}
+
 func TestErrMatchNotFound(t *testing.T) {
 	emptyHandler := func(w http.ResponseWriter, r *http.Request) {}
 
@@ -2784,6 +2802,23 @@ func TestMethodNotAllowed(t *testing.T) {
 	}
 }
 
+func TestMethodNotAllowedSubrouterWithSeveralRoutes(t *testing.T) {
+	handler := func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }
+
+	router := NewRouter()
+	subrouter := router.PathPrefix("/v1").Subrouter()
+	subrouter.HandleFunc("/api", handler).Methods(http.MethodGet)
+	subrouter.HandleFunc("/api/{id}", handler).Methods(http.MethodGet)
+
+	w := NewRecorder()
+	req := newRequest(http.MethodPut, "/v1/api")
+	router.ServeHTTP(w, req)
+
+	if w.Code != http.StatusMethodNotAllowed {
+		t.Errorf("Expected status code 405 (got %d)", w.Code)
+	}
+}
+
 type customMethodNotAllowedHandler struct {
 	msg string
 }