Skip to content

Commit

Permalink
Fix for ZijianHe#292 includes unit tests.
Browse files Browse the repository at this point in the history
Enhanced debug statements
  • Loading branch information
jpravetz committed Mar 31, 2017
1 parent 54db0d4 commit fdd9106
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 31 deletions.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Create a new router.
| --- | --- | --- |
| [opts] | <code>Object</code> | |
| [opts.prefix] | <code>String</code> | prefix router paths |
| [opts.useFirstMatch] | <code>Boolean</code> | if set then use only the first matching route |

**Example**
Basic usage:
Expand Down Expand Up @@ -173,6 +174,28 @@ router.get('/', ...); // responds to "/users"
router.get('/:id', ...); // responds to "/users/:id"
```

#### Router useFirstMatch

Only use the middleware for the first matching route:

```javascript
var router = new Router({
useFirstMatch: true
});

router.get('/api/users/count', ...); // responds to "/users/count"
router.get('/api/users/:id', ...); // responds to "/users/:id"
```

Default behaviour

```javascript
var router = new Router();

router.get('/api/users/count', ...); // executes middleware for "/users/count"
router.get('/api/users/:id', ...); // now executes middleware for "/users/count" and "/users/:id"
```

#### URL parameters

Named route parameters are captured and added to `ctx.params`.
Expand Down
17 changes: 8 additions & 9 deletions lib/layer.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
var createDebug = require('debug');
var debug = createDebug('koa-router');
var debug = require('debug')('koa-router');
var pathToRegExp = require('path-to-regexp');

createDebug.formatters.m = function(v) {
if( typeof v === 'function') {
return '['+(v.name?v.name:'anonymous')+']';
} else {
return JSON.stringify(v.map( function(m) { return m.name.length ? m.name : 'anonymous'}));
}
}
// createDebug.formatters.m = function(v) {
// if( typeof v === 'function') {
// return '['+(v.name?v.name:'anonymous')+']';
// } else {
// return JSON.stringify(v.map( function(m) { return m.name.length ? m.name : 'anonymous'}));
// }
// }

module.exports = Layer;

Expand Down
65 changes: 43 additions & 22 deletions lib/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,23 @@
* @link https://github.com/alexmingoia/koa-router
*/

var debug = require('debug')('koa-router');
var createDebug = require('debug');
var debug = createDebug('koa-router');
var compose = require('koa-compose');
var HttpError = require('http-errors');
var methods = require('methods');
var Layer = require('./layer');
var pathToRegExp = require('path-to-regexp');

createDebug.formatters.m = function (v) {
if (typeof v === 'function') {
return '[' + (v.name ? v.name : 'anonymous') + ']';
} else {
return JSON.stringify(v.map(function (m) {
return m.name.length ? m.name : 'anonymous'
}));
}
}

/**
* @module koa-router
Expand Down Expand Up @@ -41,21 +53,21 @@ module.exports = Router;
* @constructor
*/

function Router(opts) {
function Router (opts) {
if (!(this instanceof Router)) {
return new Router(opts);
}

this.opts = opts || {};
this.methods = this.opts.methods || [
'HEAD',
'OPTIONS',
'GET',
'PUT',
'PATCH',
'POST',
'DELETE'
];
'HEAD',
'OPTIONS',
'GET',
'PUT',
'PATCH',
'POST',
'DELETE'
];

this.params = {};
this.stack = [];
Expand Down Expand Up @@ -301,7 +313,7 @@ Router.prototype.prefix = function (prefix) {
Router.prototype.routes = Router.prototype.middleware = function () {
var router = this;

var dispatch = function dispatch(ctx, next) {
var dispatch = function dispatch (ctx, next) {
debug('%s %s', ctx.method, ctx.path);

var path = router.opts.routerPath || ctx.routerPath || ctx.path;
Expand All @@ -316,18 +328,23 @@ Router.prototype.routes = Router.prototype.middleware = function () {

if (!matched.route) return next();

var mostSpecificPath = matched.pathAndMethod[matched.pathAndMethod.length - 1].path;
var mspIndex = router.opts.useFirstMatch ? 0 : matched.pathAndMethod.length - 1;
var mostSpecificPath = matched.pathAndMethod[mspIndex].path;

ctx._matchedRoute = mostSpecificPath;

layerChain = matched.pathAndMethod.reduce(function(memo, layer) {
memo.push(function routerParams(ctx, next) {
var pathAndMethod = router.opts.useFirstMatch ? [matched.pathAndMethod[0]] : matched.pathAndMethod;
layerChain = pathAndMethod.reduce(function (memo, layer) {
memo.push(function routerParams (ctx, next) {
ctx.captures = layer.captures(path, ctx.captures);
ctx.params = layer.params(path, ctx.captures, ctx.params);
return next();
});
return memo.concat(layer.stack);
}, []);

debug('matched route %s %m', mostSpecificPath, layerChain);

return compose(layerChain)(ctx, next);
};

Expand All @@ -353,17 +370,19 @@ Router.prototype.routes = Router.prototype.middleware = function () {
*
* @param {Object=} options
* @param {Boolean=} options.throw throw error instead of setting status and header
* @param {Function=} options.notImplemented throw the returned value in place of the default NotImplemented error
* @param {Function=} options.methodNotAllowed throw the returned value in place of the default MethodNotAllowed error
* @param {Function=} options.notImplemented throw the returned value in place of the default
* NotImplemented error
* @param {Function=} options.methodNotAllowed throw the returned value in place of the default
* MethodNotAllowed error
* @returns {Function}
*/

Router.prototype.allowedMethods = function (options) {
options = options || {};
var implemented = this.methods;

return function allowedMethods(ctx, next) {
return next().then(function() {
return function allowedMethods (ctx, next) {
return next().then(function () {
var allowed = {};

if (!ctx.status || ctx.status === 404) {
Expand All @@ -379,7 +398,8 @@ Router.prototype.allowedMethods = function (options) {
if (options.throw) {
var notImplementedThrowable;
if (typeof options.notImplemented === 'function') {
notImplementedThrowable = options.notImplemented(); // set whatever the user returns from their function
notImplementedThrowable = options.notImplemented(); // set whatever the user returns
// from their function
} else {
notImplementedThrowable = new HttpError.NotImplemented();
}
Expand All @@ -396,7 +416,8 @@ Router.prototype.allowedMethods = function (options) {
if (options.throw) {
var notAllowedThrowable;
if (typeof options.methodNotAllowed === 'function') {
notAllowedThrowable = options.methodNotAllowed(); // set whatever the user returns from their function
notAllowedThrowable = options.methodNotAllowed(); // set whatever the user returns
// from their function
} else {
notAllowedThrowable = new HttpError.MethodNotAllowed();
}
Expand Down Expand Up @@ -541,7 +562,7 @@ Router.prototype.register = function (path, methods, middleware, opts) {
Router.prototype.route = function (name) {
var routes = this.stack;

for (var len = routes.length, i=0; i<len; i++) {
for (var len = routes.length, i = 0; i < len; i++) {
if (routes[i].name && routes[i].name === name) {
return routes[i];
}
Expand Down Expand Up @@ -674,5 +695,5 @@ Router.prototype.param = function (param, middleware) {
* @returns {String}
*/
Router.url = function (path, params) {
return Layer.prototype.url.call({path: path}, params);
return Layer.prototype.url.call({ path: path }, params);
};
54 changes: 54 additions & 0 deletions test/lib/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,60 @@ describe('Router', function () {
})
});

it('executes all matching routes middleware', function (done) {
var app = new Koa();
var router = new Router();

router
.get('/user/count', function getCount (ctx, next) {
ctx.body = ctx.body || {};
ctx.body.count = true;
return next();
})
.get('/user/:id', function getUser (ctx, next) {
ctx.body = ctx.body || {};
ctx.body.getId = true;
return next();
});

request(http.createServer(app.use(router.routes()).callback()))
.get('/user/count')
.expect(200)
.end(function (err, res) {
if (err) return done(err);
expect(res.body).to.have.property('count', true);
expect(res.body).to.have.property('getId', true);
done();
})
});

it('executes last matching route middleware', function (done) {
var app = new Koa();
var router = new Router({useFirstMatch:true});

router
.get('/user/count', function getCount (ctx, next) {
ctx.body = ctx.body || {};
ctx.body.count = true;
return next();
})
.get('/user/:id', function getUser (ctx, next) {
ctx.body = ctx.body || {};
ctx.body.getId = true;
return next();
});

request(http.createServer(app.use(router.routes()).callback()))
.get('/user/count')
.expect(200)
.end(function (err, res) {
if (err) return done(err);
expect(res.body).to.have.property('count', true);
expect(res.body).to.not.have.property('getId');
done();
})
});

it('does not run subsequent middleware without calling next', function (done) {
var app = new Koa();
var router = new Router();
Expand Down

0 comments on commit fdd9106

Please sign in to comment.