diff --git a/docs/doc.md b/docs/doc.md index 0dd86684..b115a4b0 100644 --- a/docs/doc.md +++ b/docs/doc.md @@ -9,6 +9,7 @@ - [Using GET, POST, PUT, PATCH, DELETE and OPTIONS](#using-get-post-put-patch-delete-and-options) - [Parameters in path](#parameters-in-path) - [Querystring parameters](#querystring-parameters) + - [Retrieving the path of a registered handler](#retrieving-the-path-of-a-registered-handler) - [Multipart/Urlencoded Form](#multiparturlencoded-form) - [Another example: query + post form](#another-example-query--post-form) - [Map as querystring or postform parameters](#map-as-querystring-or-postform-parameters) @@ -184,6 +185,29 @@ func main() { } ``` +### Retrieving the path of a registered handler + +Once a handler is registered with the router, you can retrieve its path with the `PathFor` method: + +```go +func main() { + // Creates a gin router with default middleware: + // logger and recovery (crash-free) middleware + router := gin.Default() + + getUser := func(c *Context) { + // Handle the GET request. + } + router.GET("/users/:name", getUser) + path := router.PathFor(getUser, ":name", "gopher") + + print(path) // Prints /users/gopher + + router.Run() +} +router := gin.Default() +``` + ### Multipart/Urlencoded Form ```go diff --git a/gin.go b/gin.go index 1965a429..da6778ca 100644 --- a/gin.go +++ b/gin.go @@ -384,6 +384,24 @@ func (engine *Engine) Routes() (routes RoutesInfo) { return routes } +// Routes returns a slice of registered routes, including some useful information, such as: +// the http method, path and the handler name. +func (engine *Engine) Route(handler HandlerFunc) (route RouteInfo, ok bool) { + handlerName := nameOfFunction(handler) + routes := RoutesInfo{} + for _, tree := range engine.trees { + routes = iterate("", tree.method, routes, tree.root) + + for _, route := range routes { + if route.Handler == handlerName { + return route, true + } + } + } + + return RouteInfo{}, false +} + func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo { path += root.path if len(root.handlers) > 0 { @@ -658,6 +676,43 @@ func (engine *Engine) HandleContext(c *Context) { c.handlers = oldHandlers } +// PathFor returns the path registered for the specified handler function. +// Route values are passed pair-wise as key value. +func (engine *Engine) PathFor(handler HandlerFunc, values ...interface{}) string { + route, ok := engine.Route(handler) + + if !ok || len(route.Path) == 0 || len(values)%2 != 0 { + return "" + } + + url := route.Path + params := make(map[string]string) + if len(values) > 0 { + key := "" + for k, v := range values { + if k%2 == 0 { + key = fmt.Sprint(v) + } else { + params[key] = fmt.Sprint(v) + } + } + } + urls := strings.Split(url, "/") + for _, v := range urls { + if v == "" { + continue + } + if v[0:1] == ":" { + if u, ok := params[v]; ok { + delete(params, v) + url = strings.Replace(url, v, u, 1) + } + } + } + + return url + toQuerystring(params) +} + func (engine *Engine) handleHTTPRequest(c *Context) { httpMethod := c.Request.Method rPath := c.Request.URL.Path @@ -786,3 +841,14 @@ func redirectRequest(c *Context) { http.Redirect(c.Writer, req, rURL, code) c.writermem.WriteHeaderNow() } + +func toQuerystring(params map[string]string) string { + if len(params) == 0 { + return "" + } + u := "?" + for k, v := range params { + u += k + "=" + v + "&" + } + return strings.TrimRight(u, "&") +} diff --git a/gin_test.go b/gin_test.go index be076537..e4594394 100644 --- a/gin_test.go +++ b/gin_test.go @@ -825,6 +825,25 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) { } } +func TestPathFor(t *testing.T) { + r := New() + + getIndex := func(c *Context) {} + r.GET("/", getIndex) + + users := r.Group("/users") + + postUser := func(c *Context) {} + users.POST("", postUser) + + getUser := func(c *Context) {} + users.GET("/:name", getUser) + + assert.Equal(t, "/", r.PathFor(getIndex)) + assert.Equal(t, "/users", r.PathFor(postUser)) + assert.Equal(t, "/users/gopher", r.PathFor(getUser, ":name", "gopher")) +} + func parseCIDR(cidr string) *net.IPNet { _, parsedCIDR, err := net.ParseCIDR(cidr) if err != nil {