From 92ffbf5cbe49df09d9c59a6081285c12fe5943b4 Mon Sep 17 00:00:00 2001 From: Tomas Della Vedova Date: Mon, 26 Mar 2018 19:18:37 +0200 Subject: [PATCH] feat(router): add ignoreTrailingSlash router option (#1632) --- lib/router.js | 6 +++ lib/routerRegistryRadix.js | 7 ++- test/router.test.js | 25 ++++++++++ test/routerRegistryRadix.test.js | 18 +++++++ test/server.test.js | 82 +++++++++++++++++++++++++++++++- 5 files changed, 135 insertions(+), 3 deletions(-) diff --git a/lib/router.js b/lib/router.js index b6f6db8ec..32a280b0e 100644 --- a/lib/router.js +++ b/lib/router.js @@ -33,12 +33,18 @@ var ResourceNotFoundError = errors.ResourceNotFoundError; * @param {Boolean} [options.strictNext=false] - Throws error when next() is * called more than once, enabled onceNext option * @param {Object} [options.registry] - route registry + * @param {Object} [options.ignoreTrailingSlash] - ignore trailing slash on + * paths */ function Router(options) { assert.object(options, 'options'); assert.object(options.log, 'options.log'); assert.optionalBool(options.onceNext, 'options.onceNext'); assert.optionalBool(options.strictNext, 'options.strictNext'); + assert.optionalBool( + options.ignoreTrailingSlash, + 'options.ignoreTrailingSlash' + ); EventEmitter.call(this); diff --git a/lib/routerRegistryRadix.js b/lib/routerRegistryRadix.js index 5a291a329..6b04d8bdd 100644 --- a/lib/routerRegistryRadix.js +++ b/lib/routerRegistryRadix.js @@ -9,9 +9,12 @@ var Chain = require('./chain'); * * @class RouterRegistryRadix * @public + * @param {Object} options - an options object + * @param {Object} [options.ignoreTrailingSlash] - ignore trailing slash on + * paths */ -function RouterRegistryRadix() { - this._findMyWay = new FindMyWay(); +function RouterRegistryRadix(options) { + this._findMyWay = new FindMyWay(options); this._routes = {}; } diff --git a/test/router.test.js b/test/router.test.js index 27d551095..002f60017 100644 --- a/test/router.test.js +++ b/test/router.test.js @@ -346,3 +346,28 @@ test('toString()', function(t) { ); t.end(); }); + +test('toString() with ignoreTrailingSlash', function(t) { + function handler(req, res, next) { + res.send('Hello world'); + } + + var router = new Router({ + log: {}, + ignoreTrailingSlash: true + }); + router.mount({ method: 'GET', path: '/' }, [handler]); + router.mount({ method: 'GET', path: '/a' }, [handler]); + router.mount({ method: 'GET', path: '/a/b' }, [handler]); + router.mount({ method: 'POST', path: '/' }, [handler]); + + t.deepEqual( + router.toString(), + '└── / (GET|POST)\n' + + ' └── a (GET)\n' + + ' └── / (GET)\n' + + ' └── b (GET)\n' + + ' └── / (GET)\n' + ); + t.end(); +}); diff --git a/test/routerRegistryRadix.test.js b/test/routerRegistryRadix.test.js index b2684d9c9..b7c40a3d7 100644 --- a/test/routerRegistryRadix.test.js +++ b/test/routerRegistryRadix.test.js @@ -99,3 +99,21 @@ test('toString()', function(t) { ); t.end(); }); + +test('toString() with ignoreTrailingSlash', function(t) { + var registry = new RouterRegistryRadix({ ignoreTrailingSlash: true }); + registry.add(getTestRoute({ method: 'GET', path: '/' })); + registry.add(getTestRoute({ method: 'GET', path: '/a' })); + registry.add(getTestRoute({ method: 'GET', path: '/a/b' })); + registry.add(getTestRoute({ method: 'POST', path: '/' })); + + t.deepEqual( + registry.toString(), + '└── / (GET|POST)\n' + + ' └── a (GET)\n' + + ' └── / (GET)\n' + + ' └── b (GET)\n' + + ' └── / (GET)\n' + ); + t.end(); +}); diff --git a/test/server.test.js b/test/server.test.js index babbdf151..d83722cac 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -46,7 +46,8 @@ before(function(cb) { dtrace: helper.dtrace, handleUncaughtExceptions: true, log: helper.getLog('server'), - version: ['2.0.0', '0.5.4', '1.4.3'] + version: ['2.0.0', '0.5.4', '1.4.3'], + ignoreTrailingSlash: true }); SERVER.listen(PORT, '127.0.0.1', function() { PORT = SERVER.address().port; @@ -159,6 +160,85 @@ test('get (path only)', function(t) { }); }); +test('get (path only - with trailing slash)', function(t) { + SERVER.get('/foo/', function echoId(req, res, next) { + res.send(); + next(); + }); + + var count = 0; + + CLIENT.get('/foo/', function(err, _, res) { + t.ifError(err); + t.equal(res.statusCode, 200); + + if (++count === 2) { + t.end(); + } + }); + + CLIENT.get('/foo', function(err, _, res) { + t.ifError(err); + t.equal(res.statusCode, 200); + + if (++count === 2) { + t.end(); + } + }); +}); + +test('get (path only - with trailing slash and nested route)', function(t) { + SERVER.get('/foo/', function echoId(req, res, next) { + res.statusCode = 200; + res.send(); + next(); + }); + + SERVER.get('/foo/bar', function echoId(req, res, next) { + res.statusCode = 201; + res.send(); + next(); + }); + + var count = 0; + + CLIENT.get('/foo/', function(err, _, res) { + t.ifError(err); + t.equal(res.statusCode, 200); + + if (++count === 4) { + t.end(); + } + }); + + CLIENT.get('/foo', function(err, _, res) { + t.ifError(err); + t.equal(res.statusCode, 200); + + if (++count === 4) { + t.end(); + } + }); + + CLIENT.get('/foo/bar/', function(err, _, res) { + t.ifError(err); + t.equal(res.statusCode, 201); + + if (++count === 4) { + t.end(); + } + }); + + CLIENT.get('/foo/bar', function(err, _, res) { + t.ifError(err); + t.equal(res.statusCode, 201); + + if (++count === 4) { + t.end(); + } + }); +}); + test('use + get (path only)', function(t) { SERVER.use(function(req, res, next) { next();