diff --git a/go.mod b/go.mod index 26cc80e7..5d0f583f 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module github.com/julienschmidt/httprouter go 1.7 + +require github.com/stretchr/testify v1.8.3 diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..c3467cea --- /dev/null +++ b/go.sum @@ -0,0 +1,17 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/router.go b/router.go index 1eab403d..ee7dadf0 100644 --- a/router.go +++ b/router.go @@ -6,30 +6,30 @@ // // A trivial example is: // -// package main +// package main // -// import ( -// "fmt" -// "github.com/julienschmidt/httprouter" -// "net/http" -// "log" -// ) +// import ( +// "fmt" +// "github.com/julienschmidt/httprouter" +// "net/http" +// "log" +// ) // -// func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { -// fmt.Fprint(w, "Welcome!\n") -// } +// func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { +// fmt.Fprint(w, "Welcome!\n") +// } // -// func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { -// fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name")) -// } +// func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { +// fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name")) +// } // -// func main() { -// router := httprouter.New() -// router.GET("/", Index) -// router.GET("/hello/:name", Hello) +// func main() { +// router := httprouter.New() +// router.GET("/", Index) +// router.GET("/hello/:name", Hello) // -// log.Fatal(http.ListenAndServe(":8080", router)) -// } +// log.Fatal(http.ListenAndServe(":8080", router)) +// } // // The router matches incoming requests by the request method and the path. // If a handle is registered for this path and method, the router delegates the @@ -39,45 +39,50 @@ // // The registered path, against which the router matches incoming requests, can // contain two types of parameters: -// Syntax Type -// :name named parameter -// *name catch-all parameter +// +// Syntax Type +// :name named parameter +// *name catch-all parameter // // Named parameters are dynamic path segments. They match anything until the // next '/' or the path end: -// Path: /blog/:category/:post // -// Requests: -// /blog/go/request-routers match: category="go", post="request-routers" -// /blog/go/request-routers/ no match, but the router would redirect -// /blog/go/ no match -// /blog/go/request-routers/comments no match +// Path: /blog/:category/:post +// +// Requests: +// /blog/go/request-routers match: category="go", post="request-routers" +// /blog/go/request-routers/ no match, but the router would redirect +// /blog/go/ no match +// /blog/go/request-routers/comments no match // // Catch-all parameters match anything until the path end, including the // directory index (the '/' before the catch-all). Since they match anything // until the end, catch-all parameters must always be the final path element. -// Path: /files/*filepath // -// Requests: -// /files/ match: filepath="/" -// /files/LICENSE match: filepath="/LICENSE" -// /files/templates/article.html match: filepath="/templates/article.html" -// /files no match, but the router would redirect +// Path: /files/*filepath +// +// Requests: +// /files/ match: filepath="/" +// /files/LICENSE match: filepath="/LICENSE" +// /files/templates/article.html match: filepath="/templates/article.html" +// /files no match, but the router would redirect // // The value of parameters is saved as a slice of the Param struct, consisting // each of a key and a value. The slice is passed to the Handle func as a third // parameter. // There are two ways to retrieve the value of a parameter: -// // by the name of the parameter -// user := ps.ByName("user") // defined by :user or *user // -// // by the index of the parameter. This way you can also get the name (key) -// thirdKey := ps[2].Key // the name of the 3rd parameter -// thirdValue := ps[2].Value // the value of the 3rd parameter +// // by the name of the parameter +// user := ps.ByName("user") // defined by :user or *user +// +// // by the index of the parameter. This way you can also get the name (key) +// thirdKey := ps[2].Key // the name of the 3rd parameter +// thirdValue := ps[2].Value // the value of the 3rd parameter package httprouter import ( "context" + "fmt" "net/http" "strings" "sync" @@ -366,7 +371,8 @@ func (r *Router) HandlerFunc(method, path string, handler http.HandlerFunc) { // of the Router's NotFound handler. // To use the operating system's file system implementation, // use http.Dir: -// router.ServeFiles("/src/*filepath", http.Dir("/var/www")) +// +// router.ServeFiles("/src/*filepath", http.Dir("/var/www")) func (r *Router) ServeFiles(path string, root http.FileSystem) { if len(path) < 10 || path[len(path)-10:] != "/*filepath" { panic("path must end with /*filepath in path '" + path + "'") @@ -538,3 +544,12 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { http.NotFound(w, req) } } + +// ListRoutes - returns list of registered routes +func (r *Router) ListRoutes() []string { + var routeList []string + for method, node := range r.trees { + routeList = append(routeList, fmt.Sprintf("%s %s", method, node.path)) + } + return routeList +} diff --git a/router_test.go b/router_test.go index ae7d2435..93bd9690 100644 --- a/router_test.go +++ b/router_test.go @@ -11,6 +11,8 @@ import ( "net/http/httptest" "reflect" "testing" + + "github.com/stretchr/testify/assert" ) type mockResponseWriter struct{} @@ -697,3 +699,14 @@ func TestRouterServeFiles(t *testing.T) { t.Error("serving file failed") } } +func Test_ListRoutes(t *testing.T) { + + handler := func(_ http.ResponseWriter, _ *http.Request, _ Params) {} + router := New() + assert.Empty(t, router.ListRoutes()) + router.Handle("GET", "/test/", handler) + routeList := router.ListRoutes() + if assert.NotEmpty(t, routeList) { + assert.Equal(t, "GET /test/", routeList[0]) + } +}