Return "route path" in context


#1

Use case: creating metrics for REST requests

We have middleware generating metrics for REST requests (rate, duration, errors). Each metric has labels, one of which is the URL. We would like that label to be the “colonized” route path (e.g. /xxx/v1/yyyy/:param/stuff) instead of the actual URL (e.g. /xxx/v1/yyyy/something/stuff) so that they group together in a reasonable manner.

This has turned out to be more difficult that we anticipated. The router code knows but does not return the data, instead it sets the actual path in the context object. We’re not seeing a reasonable way to handle this in middleware which occurs after the router has done its thing.

I have changes that should make this work for us. I’d like to push in a PR and see if it’s accepted. In the interim, here are the changes:

diff --git a/context.go b/context.go
index cf780c5..7222660 100644
--- a/context.go
+++ b/context.go
@@ -47,6 +47,9 @@ type (
                // SetPath sets the registered path for the handler.
                SetPath(p string)
 
+               // Return the original route path if different from the registered path.
+               RoutePath() string
+
                // Param returns path parameter by name.
                Param(name string) string
 
@@ -192,6 +195,7 @@ type (
                request  *http.Request
                response *Response
                path     string
+               rPath    string
                pnames   []string
                pvalues  []string
                query    url.Values
@@ -275,6 +279,10 @@ func (c *context) SetPath(p string) {
        c.path = p
 }
 
+func (c *context) RoutePath() string {
+       return c.rPath
+}
+
 func (c *context) Param(name string) string {
        for i, n := range c.pnames {
                if i < len(c.pvalues) {
diff --git a/router.go b/router.go
index ff53da8..3645be1 100644
--- a/router.go
+++ b/router.go
@@ -1,5 +1,7 @@
 package echo
 
+import "strings"
+
 type (
        // Router is the registry of all registered routes for an `Echo` instance for
        // request matching and URL path parameter parsing.
@@ -364,6 +366,10 @@ func (r *Router) Find(method, path string, c Context) {
                // Param node
        Param:
                if child = cn.findChildByKind(pkind); child != nil {
+                       if child.ppath != "" && strings.HasPrefix(child.ppath, ctx.rPath) {
+                               ctx.rPath = child.ppath
+                       }
+
                        // Issue #378
                        if len(pvalues) == n {

Any thoughts on this?