Skip to content

Conversation

maknahar
Copy link

This is my attempt to handle the Issue #271 . All existing tests are passing and I have added an additional test for current feature.

This is my first major contribution to a big open source project. Please feel free to suggest any change.

mux.go Outdated
@@ -545,3 +555,12 @@ func matchMapWithRegex(toCheck map[string]*regexp.Regexp, toMatch map[string][]s
}
return true
}

// MethodNotAllowed replies to the request with an HTTP status code 405.
func MethodNotAllowed(w http.ResponseWriter, r *http.Request) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this needs to be exported

mux.go Outdated

// MethodNotAllowedHandler returns a simple request handler
// that replies to each request with a status code 405.
func MethodNotAllowedHandler() http.Handler { return http.HandlerFunc(MethodNotAllowed) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably doesn't need to be exported either

mux.go Outdated

// MethodMismatch flag is set to true if a route is matched but there
// is a mismatch in the request method and route method
MethodMismatch bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it would be better to have a MatchError error field instead. Then there could be a ErrMethodMismatch value used there in the case of methodMatcher, and then it could be extended with other error types in the future.

@kisielk
Copy link
Contributor

kisielk commented Aug 18, 2017

Overall approach looks good to me, I left some comments about possible improvements. What do you think?

@maknahar
Copy link
Author

All changes suggested by you make perfect sense @kisielk . I will make the change ASAP.

@maknahar
Copy link
Author

@kisielk I have made the changes. Please let me know if anymore improvement is required.

@kisielk
Copy link
Contributor

kisielk commented Aug 21, 2017

@elithrar can you give this a second look ?

@kisielk kisielk requested a review from elithrar August 21, 2017 15:55
Copy link
Contributor

@elithrar elithrar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See suggested changes & commentary.

mux.go Outdated
@@ -13,6 +13,10 @@ import (
"strings"
)

var (
ErrMethodMismatch = errors.New("Method is Not Allowed")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we don't use the text for this, we should either:

  • Lowercase this as convention - e.g. method is not allowed
  • Use an enum to reflect the type of error it is.

String errors are inflexible and fragile.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will Change it to lower case.

mux_test.go Outdated
@@ -1871,3 +1871,29 @@ func newRequest(method, url string) *http.Request {
}
return req
}

func TestNoMatchMethodErrors(t *testing.T) {
func1 := func(w http.ResponseWriter, r *http.Request) {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call it handler

old_test.go Outdated
} else {
t.Errorf("Should not match.")
}
t.Errorf("Match Expectation: %v\nGot: %v\nRequest: %v %v", shouldMatch, matched, request.Method, url)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow the format used elsewhere in this file - e.g. Expected: %v Got: %v

// MatchErr is set to appropriate matching error
// It is set to ErrMethodMismatch if there is a mismatch in
// the request method and route method
MatchErr error
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't feel right: if you provide an error here you can't are extremely limited in how you can customize 405 responses.

Should we not offer a MethodNotAllowedHandler field on the Router akin to NotFoundHandler that a package user can customize? See https://godoc.org/github.com/gorilla/mux#Router

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The purpose of this is not to customize the response, but a way for the match to signal to the router the reason why it failed to match anything. The other option is to have a flag on RouteMatch specifically for the method not matching, but that feels less extensible to me.

I agree that the router should ultimately have a way to customize the handler, but that's a separate bit of logic than this.

Copy link
Author

@maknahar maknahar Aug 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that router should have MethodNotAllowedHandler. We can default to local methodNotAllowedHandler if it is not set, same as we do with http.NotFoundHandler. Should we make those change in a current fix #271 is the question.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@elithrar @kisielk Can we look into merging this PR if no other change is required? Or do you guys want to add methodNotAllowedHandler in this as well?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maknahar If you can add that as well, we can merge - that way it's feature-complete :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

@maknahar
Copy link
Author

@elithrar I have added custom MethodNotAllowedHandler in Router. Please have a look.

Copy link
Contributor

@elithrar elithrar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor documentation change, but otherwise LGTM. @kisielk for final approval/merge.

mux.go Outdated
@@ -39,6 +43,10 @@ func NewRouter() *Router {
type Router struct {
// Configurable Handler to be used when no route matches.
NotFoundHandler http.Handler

// Configurable Handler to be used request has method mismatch with route
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change to "Configurable Handler to be used when the request method does not match the route."

@kisielk
Copy link
Contributor

kisielk commented Aug 30, 2017

LGTM after the requested change.

@maknahar
Copy link
Author

@kisielk I have done the requested change.

@kisielk kisielk merged commit a659b61 into gorilla:master Aug 30, 2017
@kisielk
Copy link
Contributor

kisielk commented Aug 30, 2017

Thanks a lot!

t.Errorf("Expecting code %v", 405)
}

//Add matching route now
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor style nit. Unlike all others, this comment is missing a space between // and the text.

@silverweed
Copy link

silverweed commented Oct 2, 2017

This commit breaks the possibility to have a "catch-all" route, as it will always be called instead of any specific route with a different method.

Consider the following code:

package main

import (
	"fmt"
	"net/http"

	"github.com/gorilla/mux"
)

func main() {
	router := mux.NewRouter()
	get := router.Methods("GET").Subrouter()
	post := router.Methods("POST").Subrouter()

        // Specific POST route without parameters
	post.HandleFunc("/something", func(rw http.ResponseWriter, req *http.Request) {
		fmt.Fprintln(rw, "post")
	})
        // Catch-all route that should match any GET request, and only GET requests 
	get.HandleFunc("/{anything}", func(rw http.ResponseWriter, req *http.Request) {
		fmt.Fprintln(rw, "get")
	})

	http.Handle("/", router)
	http.ListenAndServe(":8888", nil)
}

The expected behaviour (and the actual behaviour before this commit) is:

curl localhost:8888/something -X POST  # -> post
curl localhost:8888/foobar # -> get

The actual behaviour as of this commit is that both requests are answered with GET.
It appears to only happen for routes without parameters.

This is especially problematic with routes that have the same path but different methods, as they'll all be answered with the method of the catch-all route.

The problem disappears when lines 61-64 are commented out or removed from route.go.

@silverweed
Copy link

silverweed commented Oct 2, 2017

Even worse:

post.HandleFunc("/some/{thing}", func(rw http.ResponseWriter, req *http.Request) {
	fmt.Fprintln(rw, "post")
})
 // Same route but different method
get.HandleFunc("/some/{thing}", func(rw http.ResponseWriter, req *http.Request) {
	fmt.Fprintln(rw, "get")
})
curl localhost:8888/some/thing -X POST # -> get

@silverweed
Copy link

I opened an issue for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants