Skip to content

Commit

Permalink
feature: added possibility for catching errors from async handlers an…
Browse files Browse the repository at this point in the history
…d pass them to "next" callback
  • Loading branch information
ilfroloff committed Apr 4, 2018
1 parent 6c5fe73 commit 464db19
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 4 deletions.
31 changes: 27 additions & 4 deletions core/Controller.js
Expand Up @@ -401,8 +401,17 @@ Controller.prototype._generate_url = function(method) {

this.router[method](url, function(request, response, next_route) {
eachSeries(callbacks, function(callback, next_callback, interrupt) {
/**
*
* @param {*|Error} error
*/
function unexpected_error_handler(error) {
interrupt();
next_route(error);
}

try {
callback(
var result = callback(
request,
response,
/**
Expand All @@ -419,9 +428,23 @@ Controller.prototype._generate_url = function(method) {
},
next_route
);
} catch(err) {
interrupt();
next_route(err);

if(result) {
var Class = result.constructor;

/**
* Rough detection of Promise's instance
*
* @type {boolean}
*/
var is_promise = !!(Class.all && Class.race && result.then && result.catch);

if(is_promise) {
result.catch(unexpected_error_handler);
}
}
} catch(error) {
unexpected_error_handler(error);
}
}, next_route);
});
Expand Down
@@ -0,0 +1,7 @@
module.exports = {
application: {
folders: {
controllers: 'protected/controllers_with_promised_handlers/'
}
}
};
@@ -0,0 +1,34 @@
var Promise = require('es6-promise');
var app = require('../../../../')('controllers-with-promised-handlers');
var controller = app.Controller({
name: 'with_promised_handlers',
root: '/with-promised-handlers'
});

controller.get('/resolved-with-returning-non-promise', function(request, response) {
Promise.resolve('resolved').then(function(data) {
response.ok(data);
});

return 'uninfluenced-retured-data';
});

controller.get('/resolved-with-returning-promise', function(request, response) {
return Promise.resolve('resolved').then(function(data) {
response.ok(data);
});
});

controller.get('/rejected-caught-by-handler', function(request, response) {
return Promise.reject('rejected').catch(function(data) {
response.error(data);
});
});

controller.get('/rejected-caught-by-ifnode', function() {
return Promise.reject('rejected');
});

controller.error(function(error, request, response) {
response.error('default-error-handler:' + error)
});
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -28,6 +28,7 @@
"devDependencies": {
"body-parser": "1.17.2",
"coveralls": "2.13.1",
"es6-promise": "4.2.4",
"eslint": "4.5.0",
"eslint-plugin-require-jsdoc": "1.0.4",
"istanbul": "0.4.5",
Expand Down
36 changes: 36 additions & 0 deletions test/05. controllers.js
Expand Up @@ -346,4 +346,40 @@ describe('Controllers', function() {
.expect(500, done);
})
});

describe('Controller with promised handlers', function() {
/**
*
* @type {Application}
*/
var app = IFNode({
project_folder: Path.resolve(__dirname, '../examples/controllers'),
alias: 'controllers-with-promised-handlers',
environment: 'controllers-with-promised-handlers'
}).load();

it('should return "resolved" with 200 status (non-promise is returned from handler)', function(done) {
SuperTest(app.listener)
.get('/with-promised-handlers/resolved-with-returning-non-promise')
.expect(200, 'resolved', done);
});

it('should return "resolved" with 200 status (promise is returned from handler)', function(done) {
SuperTest(app.listener)
.get('/with-promised-handlers/resolved-with-returning-promise')
.expect(200, 'resolved', done);
});

it('should return "rejected" with 500 status', function(done) {
SuperTest(app.listener)
.get('/with-promised-handlers/rejected-caught-by-handler')
.expect(500, 'rejected', done);
});

it('should return "default-error:rejected" with 500 status by common controller\'s handler', function(done) {
SuperTest(app.listener)
.get('/with-promised-handlers/rejected-caught-by-ifnode')
.expect(500, 'default-error-handler:rejected', done);
});
});
});

0 comments on commit 464db19

Please sign in to comment.