diff --git a/examples/01-basic/routes.js b/examples/01-basic/routes.js index 39905a2..cfba368 100644 --- a/examples/01-basic/routes.js +++ b/examples/01-basic/routes.js @@ -2,6 +2,14 @@ const path = require('path') module.exports = { router: [ + { + type: 'ROUTE', + method: ['GET'], + path: '/error-page', + callback: (req, res) => { + throw new Error('Simulate 500') + } + }, { type: 'ROUTE', method: ['GET'], diff --git a/src/assets/default_pages/4xx.html b/src/assets/default_pages/4xx.html new file mode 100644 index 0000000..2560154 --- /dev/null +++ b/src/assets/default_pages/4xx.html @@ -0,0 +1,43 @@ + + + + + + +
+
+
+ 404 +
+
The page could not be found
+
+
+ + diff --git a/src/assets/default_pages/5xx.html b/src/assets/default_pages/5xx.html new file mode 100644 index 0000000..2fb16a0 --- /dev/null +++ b/src/assets/default_pages/5xx.html @@ -0,0 +1,41 @@ + + + + + + +
+
+
+ 500 +
+
The page could not be found
+
+
+ + diff --git a/src/cherry.js b/src/cherry.js index abfc161..48e8906 100644 --- a/src/cherry.js +++ b/src/cherry.js @@ -4,6 +4,7 @@ const HookConfigurator = require('./configuration/HookConfigurator') const MiddlewareConfigurator = require('./configuration/MiddlewareConfigurator') const RouteConfigurator = require('./configuration/RouteConfigurator') const RedirectionConfigurator = require('./configuration/RedirectionConfigurator') +const DefaultErrorPageConfigurator = require('./configuration/DefaultErrorPageConfigurator') const ORMManager = require('./orm/ORMManager') const CherryServerManager = require('./server/CherryServerManager') @@ -16,6 +17,7 @@ class Cherry { this.middlewareConfigurator = new MiddlewareConfigurator() this.routeConfigurator = new RouteConfigurator() this.redirectionConfigurator = new RedirectionConfigurator() + this.defaultErrorPageConfigurator = new DefaultErrorPageConfigurator() this.ormManager = new ORMManager(this) this.cherryServerManager = new CherryServerManager(this) @@ -26,15 +28,12 @@ class Cherry { * @param {Object} options The options to configure Cherry */ configure (options = {}) { - // if (check.isDefined(options, 'onError')) { - // this.dispatcher.onError(options.onError) - // } - this.pluginConfigurator.configure(options) this.hookConfigurator.configure(options) this.middlewareConfigurator.configure(options) this.routeConfigurator.configure(options) this.redirectionConfigurator.configure(options) + this.defaultErrorPageConfigurator.configure(options) if (this.pluginConfigurator.getPlugin('DatabaseEngine') && typeof options.database !== 'undefined') { this.ormManager.setPlugin(this.pluginConfigurator.getPlugin('DatabaseEngine')) diff --git a/src/configuration/DefaultErrorPageConfigurator.js b/src/configuration/DefaultErrorPageConfigurator.js new file mode 100644 index 0000000..3f5c1d0 --- /dev/null +++ b/src/configuration/DefaultErrorPageConfigurator.js @@ -0,0 +1,51 @@ +const path = require('path') +const CherryConfigurator = require('../abstract/CherryConfigurator') +const check = require('../helpers/check') + +/** + * The configurator of the default error pages + * Inherits from the abstract CherryConfigurator + */ +class DefaultErrorPageConfigurator extends CherryConfigurator { + constructor () { + super({}) + + this.manager.clientErrorPage = this.clientErrorPage + this.manager.serverErrorPage = this.serverErrorPage + } + + /** + * Configure the default pages process in case of error + * @param {Object} options The options set to the cherry instance + */ + configure (options) { + if (check.isDefined(options, 'defaultPages')) { + Object.keys(this.manager).forEach((defaultPage) => { + if (check.isDefined(options.defaultPages, defaultPage)) { + this.manager[defaultPage] = options.defaultPages[defaultPage] + } + }) + } + } + + /** + * The default process in case of a 4xx page + * @param {CherryIncomingMessage} req The current request + * @param {CherryServerResponse} res The response object + */ + clientErrorPage (req, res) { + res.html(path.join(__dirname, '../assets/default_pages/4xx.html'), { statusCode: 404 }) + } + + /** + * The default process in case of a 5xx page + * @param {CherryIncomingMessage} req The current request + * @param {CherryServerResponse} res The response object + * @param {Error} err The error catch + */ + serverErrorPage (req, res, err) { + res.html(path.join(__dirname, '../assets/default_pages/5xx.html'), { statusCode: 500 }) + } +} + +module.exports = DefaultErrorPageConfigurator diff --git a/src/processor/Dispatcher.js b/src/processor/Dispatcher.js index 13427e0..f673449 100644 --- a/src/processor/Dispatcher.js +++ b/src/processor/Dispatcher.js @@ -38,8 +38,7 @@ class Dispatcher { this.executeRoute(matchingRouteResponse, request, response) } } else { - response.writeHead(404) - response.end('') + this.cherry.defaultErrorPageConfigurator.manager.clientErrorPage(request, response) } } @@ -78,10 +77,7 @@ class Dispatcher { request._route = matchingRouteResponse.getMatchingRoute() this.resolver.resolve(request, response) }).catch((err) => { - // @todo : manage error - console.log(err) - response.writeHead(500) - response.end('') + this.cherry.defaultErrorPageConfigurator.manager.serverErrorPage(request, response, err) }) } } diff --git a/src/processor/Resolver.js b/src/processor/Resolver.js index b973522..87d52a8 100644 --- a/src/processor/Resolver.js +++ b/src/processor/Resolver.js @@ -46,12 +46,9 @@ class Resolver { { request, response, processResult: asyncResult }, asyncResult ) - }).catch((e) => { - // @todo use the onError method - console.log('Error in _resolve', e) + }).catch((error) => { if (!response.finished) { - response.writeHead(500, { 'Content-Type': 'application/json' }) - response.end(JSON.stringify(e)) + this.cherry.defaultErrorPageConfigurator.manager.serverErrorPage(request, response, error) } }) } else { diff --git a/test/specs/configuration/DefaultErrorPageConfigurator.spec.js b/test/specs/configuration/DefaultErrorPageConfigurator.spec.js new file mode 100644 index 0000000..e7a0014 --- /dev/null +++ b/test/specs/configuration/DefaultErrorPageConfigurator.spec.js @@ -0,0 +1,39 @@ +const DefaultErrorPageConfigurator = require(path.join(__root, './src/configuration/DefaultErrorPageConfigurator')) + +const default404 = () => { + return 404 +} +let defaultErrorPageConfigurator = null +let customDefaultErrorPageConfigurator = null +const fakeResponse = { + errorCode: 0, + html: function (path, options) { + this.errorCode = options.statusCode + } +} + +describe('DefaultErrorPageConfigurator', () => { + before(() => { + defaultErrorPageConfigurator = new DefaultErrorPageConfigurator() + customDefaultErrorPageConfigurator = new DefaultErrorPageConfigurator() + }) + + it('Tests the method configure', () => { + customDefaultErrorPageConfigurator.configure({ + defaultPages: { + clientErrorPage: default404 + } + }) + }) + + it('Tests the method clientErrorPage', () => { + defaultErrorPageConfigurator.manager.clientErrorPage({}, fakeResponse) + expect(fakeResponse.errorCode).to.be.equal(404) + expect(customDefaultErrorPageConfigurator.manager.clientErrorPage({}, fakeResponse)).to.be.equal(404) + }) + + it('Tests the method serverErrorPage', () => { + defaultErrorPageConfigurator.manager.serverErrorPage({}, fakeResponse) + expect(fakeResponse.errorCode).to.be.equal(500) + }) +}) diff --git a/test/specs/processor/Dispatcher.spec.js b/test/specs/processor/Dispatcher.spec.js index 7a4d65d..746d0e0 100644 --- a/test/specs/processor/Dispatcher.spec.js +++ b/test/specs/processor/Dispatcher.spec.js @@ -1,5 +1,6 @@ /* eslint no-unused-expressions: 0 */ const Dispatcher = require(path.join(__root, './src/processor/Dispatcher')) +const DefaultErrorPageConfigurator = require(path.join(__root, './src/configuration/DefaultErrorPageConfigurator')) /* Simulation values */ @@ -28,7 +29,8 @@ let fakeCherryInstance = { } } } - } + }, + defaultErrorPageConfigurator: new DefaultErrorPageConfigurator() } let fakeRequest = { url: 'http://localhost:3000/test', @@ -44,10 +46,11 @@ let fakeRequest = { boundDataToRequest: async () => {} } let fakeResponse = { - writeHead (httpCode) { + html: () => {}, + writeHead: (httpCode) => { expect(httpCode).to.be.above(400) }, - end (response) { + end: (response) => { expect(typeof response).to.be.equal('string') } }