Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Store matched routes in request #34

Closed
wants to merge 1 commit into from

Conversation

ajfranzoia
Copy link

I'm transplanting this PR from the original I made at expressjs/express#2864 (issue discussed at expressjs/express#2501).

It allows to store every matched route in a request when travelling through nested routes. This may be useful for logging, ACL checks, etc.
So, for example, if you have your routing set like this:

var fooRouter = new Router();
var barRouter = new Router();
var bazRouter = new Router();

bazRouter.get(['/bez', '/baz/:subId'], function(req, res, next) {
  next();
});

fooRouter.use(['/foo/:id', '/foe'], barRouter);
barRouter.use(['/bar'], bazRouter);

A request for the URL /foo/10/bar/baz/30 will set an array of matched routes in the request object like this:

req.matchedRoutes = ['/foo/:id', '/bar', '/baz/:subId'];

Let me know if there is anything that needs to be modification, unmeaningful variable names, etc.

@dougwilson dougwilson added the pr label Feb 18, 2017
Copy link
Member

@wesleytodd wesleytodd left a comment

Choose a reason for hiding this comment

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

There hasn't been much discussion on this since the original thread, but I just wanted to mention that I would really like to see this feature land for the router before express@5.0.

@@ -286,6 +286,11 @@ Router.prototype.handle = function handle(req, res, callback) {
return next(layerError || err)
}

// store matched routes
if (layer.path) {
req.matchedRoutes = (req.matchedRoutes || []).concat(layer.matchedPath.path)
Copy link
Member

Choose a reason for hiding this comment

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

This is going to create unnecessary arrays. Can you instead only create a new array the first time, then push to it after?

req. matchedRoutes = req.matchedRoutes || []
req.matchedRoutes.push(layer.matchedPath.path)

Copy link
Contributor

Choose a reason for hiding this comment

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

if (path === '/' && opts.end === false) {
this.regexp.fast_slash = true
if (paths === '/' && opts.end === false) {
this.fastSlash = true
Copy link
Member

Choose a reason for hiding this comment

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

V8 can do optimizations if the shape of an object remains the same. This addition will have only some Layer's with a fastSlash property. To optimize this, you should define it as undefined above, or false here.

Copy link
Contributor

Choose a reason for hiding this comment

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

}

debug('new %s', path)
debug('new %s', paths)
var opts = options || {}

this.handle = fn
this.name = fn.name || '<anonymous>'
this.params = undefined
this.path = undefined
Copy link
Member

Choose a reason for hiding this comment

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

Add a line for this.matchedPath = undefined, see comment below for reasoning.

Copy link
Contributor

Choose a reason for hiding this comment

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

return true
}

var m = this.regexp.exec(path)
var checkPath, m;
Copy link
Member

Choose a reason for hiding this comment

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

All of the express modules are working toward compliance with standard. Which includes the rule that variable declarations are each on a single line. We have doing it piecemeal because it would cause too many conflicts otherwise, so since this is a new line, can you make the change just here?

Copy link
Contributor

Choose a reason for hiding this comment

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

@@ -985,6 +985,31 @@ describe('Router', function () {
})
})

describe('req.matchedRoutes', function() {
Copy link
Member

Choose a reason for hiding this comment

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

Same as comment above, can you make the formatting through here function () {? As per standard.

Copy link
Contributor

Choose a reason for hiding this comment

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

@wesleytodd
Copy link
Member

Also, maybe we should change the property name to matchedPaths. There is something called a Route, and it may be less ambiguous to call it this. Thoughts on that?

@wesleytodd wesleytodd mentioned this pull request Mar 25, 2017
haysmike pushed a commit to goodeggs/secure-router that referenced this pull request Aug 11, 2017
- set `__mountpath` in `.use()`
- override `process_params` to add `req.matchedRoutes`

`req.matchedRoutes` is a proposed addition to express v5:
pillarjs/router#34

Doing this because in goodeggs-stats, we need to know what route we are
currently on when we are publishing metrics to librato. In express v4
there is no mechanism to do this, if the routes have parameters in them
(like :id, etc) or if they have nesting (given /foo/bar, the middleware
for /foo has no knowledge of /bar and vice-versa).
@GuskiS
Copy link

GuskiS commented Nov 11, 2019

Is there a plan to get this merged?

@EgilsZ
Copy link

EgilsZ commented Nov 13, 2019

Can someone please give any timeline on when this could be merged? Anything blocking this to be completed?

@wesleytodd
Copy link
Member

Nothing blocking AFAIK, there are just higher priorities. If you are interested in helping out you can take a look at these and start participating in the project:

https://github.com/expressjs/express/blob/master/Contributing.md
expressjs/express#4055

More people helping collaborate and traige issues would unblock the core team and allow us to spend more time on moving these features forward. Thanks in advance for your help!

@saasen
Copy link

saasen commented Dec 5, 2019

I would love this feature, as we're trying to expose Prometheus metrics based on the resolved route, rather than the incoming URL and normalizing it (which is very error prone).

@wesleytodd
Copy link
Member

@saasen, my original interest in this feature was the exact same as your use case. If you are looking for a good solution which is simple and does not have many edge cases, you can do what I did in that app:

const onFinished = require('on-finished')
function routeMetrics (route) {
  return [route, (req, res) => {
    onFinished(res, (err) => {
      reportMetric(err, route)
    })
  }]
}

app.get(routeMetrics('/:foo'), routeHandlerMiddleware)

The only thing to be aware of with a solution like this is that if a request would match two routes and both get run, both would report. You can prevent this if it is a concern for your app by setting a flag on res like res.locals.route = route and then reference that off of res inside the onFinished handler function. Make sense?

@saasen
Copy link

saasen commented Dec 5, 2019

@wesleytodd: Would this work when defining routes inside a Router as well? AFAIK, the route resolved inside a Router doesn't contain the "parent"-route.

@dougwilson
Copy link
Contributor

For the folks who keep asking when it will be merged / what is blocking: there are open review issues at the top that haven't been addressed yet.

@wesleytodd
Copy link
Member

@saasen Hm, good question. Without actually trying that I am guessing no and that you would need to use a whole express app which has a mountpath. Maybe there is another way I am missing right now.

@dougwilson Oh yeah, I guess I did open a review! I wonder if @ajfranzoia is interested in making those updates? If not I might be able to find some time soon to take this over and clean it up.

@gabegorelick
Copy link
Contributor

I rebased these changes into #86. But it's mostly unchanged from this PR.

@gabegorelick
Copy link
Contributor

Sorry for the noise, but wanted to mark off all of the feedback from @wesleytodd to ensure that it's incorporated into #86.

gabegorelick pushed a commit to gabegorelick/router that referenced this pull request Jan 5, 2020
gabegorelick pushed a commit to gabegorelick/router that referenced this pull request Jan 5, 2020
gabegorelick pushed a commit to gabegorelick/router that referenced this pull request Jan 6, 2020
@dougwilson
Copy link
Contributor

Closing this in favor of the progress being made in #86

@dougwilson dougwilson closed this Mar 24, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants