diff --git a/README.md b/README.md index 67d2761..42638e0 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Inside the handler function you provide, access path parameters with the `PathPa ```go router.AddRoute(http.MethodGet, "user/:userID", func(w http.ResponseWriter, r *http.Request) { - params, _ := router.PathParams(requestMethod, requestPath) + params, _ := PathParams(r) userID := params["userID"] }) @@ -23,3 +23,7 @@ router.AddRoute(http.MethodGet, "user/:userID", func(w http.ResponseWriter, r *h ## Not Found Handler If Router.NotFoundHandler is not a set, a default handler will be called when a route is not found. If you want to set your own handler, set Router.NotFoundHandler with the http.Handler you would prefer. + +## Public consumption + +I have included a license but this really isn't for public consumption. If you have found this repository somehow and you want to use it, understand that you are on your own in terms of getting it working, fixing issues, adding features, etc. Use at your own risk. diff --git a/router.go b/router.go index bbdfd43..d4be689 100644 --- a/router.go +++ b/router.go @@ -112,31 +112,6 @@ func (r *Router) AddRoute(method string, path string, callback http.HandlerFunc) return } -// Handler returns the Handler to use for the given request, consulting r.Method, r.URL.Path. It -// always returns a non-nil Handler. -// -// Handler also returns the path which it matched. -// -// If there is no registered Handler that applies to the request, Handler returns a ``page not -// found'' Handler and an empty pattern. -func (r *Router) Handler(req *http.Request) (h http.Handler, pattern string) { - method := req.Method - path := req.URL.Path - - if r.NotFoundHandler == nil { - h = NotFoundHandler - } - - endpoint, _, err := r.getEndpoint(method, path) - - if err == nil { - h = endpoint.callback - pattern = endpoint.path - } - - return -} - // ServeHTTP is the function that is required by http.Handler. It takes an http.ResponseWriter which // it uses to write to a response object that will construct a response for the user. It also takes // an *http.Request which describes the request the user has made. @@ -151,15 +126,13 @@ func (r Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { endpoint, params, err := r.getEndpoint(method, path) if err != nil { - handler = NotFoundHandler + handler = r.NotFoundHandler } else { handler = endpoint.callback ctx := context.WithValue(context.Background(), paramsKey, params) req = req.WithContext(ctx) } - handler, _ = r.Handler(req) - handler.ServeHTTP(w, req) return @@ -233,6 +206,8 @@ func (r *Router) getEndpoint(method string, path string) (end *endpoint, params seg, paramName := getChild(key, curr) if seg == nil { + err = errors.New("route not found") + return } diff --git a/router_test.go b/router_test.go index 24df824..4288b0c 100644 --- a/router_test.go +++ b/router_test.go @@ -18,7 +18,7 @@ func TestAddRouter(t *testing.T) { testAddRoot(router, t) testAddOneSegment(router, t) testAddManySegments(router, t) - // TODO: add test for error when trying duplicate method + path + testAddDuplicateEndpoint(router, t) } func TestServeHTTP(t *testing.T) { @@ -29,6 +29,8 @@ func TestServeHTTP(t *testing.T) { testMatchesRoot(router, t) testMatchesLongPath(router, t) testMatchesPathParam(router, t) + testDefaultNotFound(router, t) + testCustomNotFound(router, t) } func TestPathParams(t *testing.T) { @@ -108,7 +110,7 @@ func matchAndCheckRoute(r *Router, method string, path string, expectedBody stri r.ServeHTTP(rr, request) if rr.Code != expectedCode { - err = fmt.Errorf("The returned callback did not write 200 to the header. Found %d", rr.Code) + err = fmt.Errorf("The returned callback did not write %d to the header. Found %d", expectedCode, rr.Code) return } @@ -128,6 +130,28 @@ func matchAndCheckRoute(r *Router, method string, path string, expectedBody stri return } +func testAddDuplicateEndpoint(router Router, t *testing.T) { + defer testOutcome("add duplicate endpoints", t) + + err := addAndCheckRoute(&router, http.MethodGet, "/dupe", func(http.ResponseWriter, *http.Request) {}) + + if err != nil { + t.Error("The route was not correctly added to the routoer", err) + } + + err = addAndCheckRoute(&router, http.MethodPost, "/dupe", func(http.ResponseWriter, *http.Request) {}) + + if err != nil { + t.Error("The route was not correctly added to the routoer", err) + } + + err = addAndCheckRoute(&router, http.MethodGet, "/dupe", func(http.ResponseWriter, *http.Request) {}) + + if err == nil { + t.Error("Adding a duplicate route should throw an error.") + } +} + func testAddManySegments(router Router, t *testing.T) { defer testOutcome("add many multiple segments", t) @@ -170,6 +194,53 @@ func testAddRoot(router Router, t *testing.T) { } } +func testCustomNotFound(router Router, t *testing.T) { + defer testOutcome("custom NotFoundHandler", t) + + expectedBody := "Forbidden" + expectedCode := 401 + path := "/actual/path" + + router.AddRoute(http.MethodPatch, path, func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(404) + w.Write([]byte("Not found.")) + }) + + router.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(expectedCode) + w.Write([]byte(expectedBody)) + }) + + err := matchAndCheckRoute(&router, http.MethodPatch, "/gibberish/forbidden", expectedBody, expectedCode) + + if err != nil { + t.Error("Did not call the custom handler.", err) + + return + } +} + +func testDefaultNotFound(router Router, t *testing.T) { + defer testOutcome("default NotFoundHandler", t) + + expectedBody := "Not Found." + expectedCode := 404 + path := "/gibberish" + + router.AddRoute(http.MethodDelete, path, func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(expectedCode) + w.Write([]byte(expectedBody)) + }) + + err := matchAndCheckRoute(&router, http.MethodDelete, "/gibberish", expectedBody, expectedCode) + + if err != nil { + t.Error("Did not find the expected callback handler", err) + + return + } +} + func testMatchesLongPath(router Router, t *testing.T) { defer testOutcome("match long path", t)