Skip to content

Commit

Permalink
Provide matched route to middleware
Browse files Browse the repository at this point in the history
- 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).
  • Loading branch information
Mike Hays committed Aug 11, 2017
1 parent beabcab commit 7f66410
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 1 deletion.
35 changes: 34 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ export default class Router extends BaseRouter {
return _.isFunction(router.resolveCustomSecurity);
}

if (_.isString(path)) {
const pathIsString = _.isString(path);
if (pathIsString) {
let {pathDefinition} = this.getPathDefinitionMatching(path);
if (_.isNil(pathDefinition)) {
pathDefinition = createPathDefinition({path});
Expand All @@ -147,7 +148,39 @@ export default class Router extends BaseRouter {
this.pathDefinitions.push(...innerRouter.pathDefinitions);
}
}

let offset = this.stack.length;
BaseRouter.prototype.use.apply(this, arguments);

/* so that in our monkey patch of process_params, we can know about the
* path of this part of the route */
if (pathIsString) {
for (; offset < this.stack.length; offset++) {
const layer = this.stack[offset];
// I'm not sure if my check for `fast_slash` is the way to go here
// But if I don't check for it, each stack element will add a slash to the path
if (layer && layer.regexp && !layer.regexp.fast_slash) {
layer.__mountpath = path;
}
}
}
}

process_params (layer, called, req, res, done) {
var path =
_.get(req, 'route.path',
_.get(req, 'route.regexp.source',
_.get(layer, '__mountpath',
'')));

if (req.matchedRoutes == null) req.matchedRoutes = [];
req.__route = (req.__route || '') + path;

if (!_.isEmpty(path)) {
req.matchedRoutes.push(path);
}

return BaseRouter.prototype.process_params.apply(this, arguments);
}

bouncer (bouncer) {
Expand Down
67 changes: 67 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,28 @@ describe('use()', function () {
return withRunningServer(router)
.then(() => expectRequest('GET', '/foo').toReturnCode(200));
});

it('provides req.matchedRoute to middlewares even when 404', function () {
const router = buildRouter();
const subRouter = new Router();
subRouter.secureSubpath({
path: '/nested',
bouncers: [
_.constant(Router.AUTHENTICATE),
_.constant(Router.AUTHORIZE),
],
});
router.use('/base/:baseId', subRouter);
router.use((req, res, next) => {
res.status(404);
res.json(req.matchedRoutes);
});
return withRunningServer(router)
.then(() =>
expectRequest('GET', '/base/2/nested/239847')
.toReturnBody(['/base/:baseId', '/nested'])
);
});
});

describe('secureEndpoint()', function () {
Expand Down Expand Up @@ -169,6 +191,29 @@ describe('secureEndpoint()', function () {
.then(() => expectRequest('POST', '/foo').toReturnCode(200))
.then(() => expectRequest('POST', '/foo/123').toReturnCode(403));
});

it('provides req.matchedRoute to middlewares', function () {
const router = buildRouter();
const subRouter = new Router();
subRouter.secureEndpoint({
method: 'GET',
path: '/nested/:nestedId',
bouncers: [
_.constant(Router.AUTHENTICATE),
_.constant(Router.AUTHORIZE),
],
middleware: (req, res) => {
res.json(req.matchedRoutes);
res.sendStatus(200);
},
});
router.use('/base/:baseId', subRouter);
return withRunningServer(router)
.then(() =>
expectRequest('GET', '/base/2/nested/239847')
.toReturnBody(['/base/:baseId', '/nested/:nestedId'])
);
});
});

describe('secureSubpath()', function () {
Expand Down Expand Up @@ -221,6 +266,28 @@ describe('secureSubpath()', function () {
.then(() => expectRequest('GET', '/sub/subsub/foo').toReturnCode(200))
.then(() => expectRequest('GET', '/sub/foo').toReturnCode(401));
});

it('provides req.matchedRoute to middlewares', function () {
const router = buildRouter();
const subRouter = new Router();
subRouter.secureSubpath({
path: '/nested',
bouncers: [
_.constant(Router.AUTHENTICATE),
_.constant(Router.AUTHORIZE),
],
})
.get('/:nestedId', (req, res) => {
res.json(req.matchedRoutes);
res.sendStatus(200);
});
router.use('/base/:baseId', subRouter);
return withRunningServer(router)
.then(() =>
expectRequest('GET', '/base/2/nested/239847')
.toReturnBody(['/base/:baseId', '/nested', '/:nestedId'])
);
});
});

describe('bouncer()', function () {
Expand Down

0 comments on commit 7f66410

Please sign in to comment.