diff --git a/README.md b/README.md index a498379..ec5c858 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,10 @@ Maps the specified path parameter `name` to a specialized param-capturing middle This function positions the middleware in the same stack as `.use`. +If a `Promise` is returned from the `param_middleware` function, the router +will attach an `onRejected` callback using `.then(null, onRejected)`. If the promise is rejected, +`next` will be called with the rejected value as the error, or `new Error...` if the value is falsy. + Parameter mapping is used to provide pre-conditions to routes which use normalized placeholders. For example a _:user_id_ parameter could automatically load a user's information from the database without diff --git a/index.js b/index.js index 45495b3..874d5b2 100644 --- a/index.js +++ b/index.js @@ -13,6 +13,7 @@ */ var flatten = require('array-flatten').flatten +var isPromise = require('is-promise') var Layer = require('./lib/layer') var methods = require('methods') var mixin = require('utils-merge') @@ -634,7 +635,12 @@ function processParams (params, layer, called, req, res, done) { if (!fn) return param() try { - fn(req, res, paramCallback, paramVal, key.name) + var ret = fn(req, res, paramCallback, paramVal, key.name) + if (isPromise(ret)) { + ret.then(null, function (error) { + paramCallback(error || new Error('Rejected promise')) + }) + } } catch (e) { paramCallback(e) } diff --git a/lib/layer.js b/lib/layer.js index 39acfe2..4936ca2 100644 --- a/lib/layer.js +++ b/lib/layer.js @@ -12,6 +12,7 @@ * @private */ +var isPromise = require('is-promise') var pathRegexp = require('path-to-regexp') /** @@ -187,20 +188,6 @@ function decodeParam (val) { } } -/** - * Returns true if the val is a Promise. - * - * @param {*} val - * @return {boolean} - * @private - */ - -function isPromise (val) { - return val && - typeof val === 'object' && - typeof val.then === 'function' -} - /** * Loosens the given path for path-to-regexp matching. */ diff --git a/package.json b/package.json index 9b6306c..58bc285 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "repository": "pillarjs/router", "dependencies": { "array-flatten": "3.0.0", + "is-promise": "4.0.0", "methods": "~1.1.2", "parseurl": "~1.3.3", "path-to-regexp": "3.2.0", diff --git a/test/param.js b/test/param.js index 43245fc..98bef58 100644 --- a/test/param.js +++ b/test/param.js @@ -1,4 +1,3 @@ - var after = require('after') var Router = require('..') var utils = require('./support/utils') @@ -10,6 +9,8 @@ var shouldNotHitHandle = utils.shouldNotHitHandle var createServer = utils.createServer var request = utils.request +var describePromises = global.Promise ? describe : describe.skip + describe('Router', function () { describe('.param(name, fn)', function () { it('should reject missing name', function () { @@ -253,6 +254,25 @@ describe('Router', function () { .get('/user/bob') .expect(500, /Error: boom/, done) }) + describePromises('promise support', function () { + it('should catch rejected promises returned from fn', function (done) { + var router = new Router() + var server = createServer(router) + + router.param('user', function parseUser (req, res, next, user) { + return Promise.reject(new Error('boom')) + }) + + router.get('/user/:user', function (req, res) { + res.setHeader('Content-Type', 'text/plain') + res.end('get user ' + req.params.id) + }) + + request(server) + .get('/user/bob') + .expect(500, /Error: boom/, done) + }) + }) describe('next("route")', function () { it('should cause route with param to be skipped', function (done) { diff --git a/test/router.js b/test/router.js index 74308f7..7163ee5 100644 --- a/test/router.js +++ b/test/router.js @@ -813,6 +813,24 @@ describe('Router', function () { .expect(200, 'saw Error: boom!', done) }) }) + it('should invoke error function after router.param returns rejected promise', function (done) { + var router = new Router() + var server = createServer(router) + + router.param('user', function (req, res, next, user) { + return Promise.reject(new Error('boom!')) + }) + + router.get('/:user', function handle (req, res, next) { + res.end() + }) + + router.use(sawError) + + request(server) + .get('/username') + .expect(200, 'saw Error: boom!', done) + }) }) describe('req.baseUrl', function () {