Skip to content
This repository has been archived by the owner on Nov 24, 2018. It is now read-only.

Commit

Permalink
law and order #2
Browse files Browse the repository at this point in the history
  • Loading branch information
corpix committed Jul 15, 2013
2 parents 4c68af7 + 1a6b71d commit 936e9be
Show file tree
Hide file tree
Showing 17 changed files with 262 additions and 163 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
4 changes: 0 additions & 4 deletions .travis.yml

This file was deleted.

5 changes: 5 additions & 0 deletions errors/badRequest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
name: 'badRequest',
message: 'Bad request',
status: 400
};
5 changes: 5 additions & 0 deletions errors/conflict.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
name: 'conflict',
message: 'Conflict',
status: 409
};
5 changes: 5 additions & 0 deletions errors/forbidden.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
name: 'forbidden',
message: 'Forbidden. You don\'t have permission to access this',
status: 403
};
5 changes: 5 additions & 0 deletions errors/notFound.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
name: 'notFound',
message: 'Oops! The page you requested doesn\'t exist',
status: 404
};
5 changes: 5 additions & 0 deletions errors/unauthorized.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
name: 'unauthorized',
message: 'Unauthorized',
status: 401
};
4 changes: 0 additions & 4 deletions http/BadRequest.json

This file was deleted.

4 changes: 0 additions & 4 deletions http/Conflict.json

This file was deleted.

4 changes: 0 additions & 4 deletions http/Forbidden.json

This file was deleted.

4 changes: 0 additions & 4 deletions http/NotFound.json

This file was deleted.

4 changes: 0 additions & 4 deletions http/Unauthorized.json

This file was deleted.

201 changes: 118 additions & 83 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,95 +1,130 @@
var fs = require('fs')
, path = require('path')
, basename = path.basename
, defaultLogger
, ex = {};

defaultLogger = function(err){
console.error(new Date().toLocaleString(), '>>', err);
console.log(err.stack);
}

ex.define = function(opts){
var fn = function(){
this.message = opts.message;
this.name = opts.name;
this.status = opts.status;

Error.call(this, this.message);
Error.captureStackTrace(this, arguments.callee);
}
var fs = require('fs'),
path = require('path'),
pjoin = path.join,
basename = path.basename,
defaultLogger,
hasOwn = Object.prototype.hasOwnProperty,
ex = {};

fn.prototype.__proto__ = Error.prototype;
ex.__defineGetter__(opts.name, function(){
return new fn();
});
ERRORS_DIR = pjoin(__dirname, 'errors');

return fn;
}
function Errors(app, config) {
config || (config = { });

ex.bind = function(app, opts){
if(!opts) opts = {};
opts.logger || (opts.logger = defaultLogger);
app.set('view engine') ||
(config.plain = true);

if(!app.set('view engine')) opts.plain = true;
config.list || (config.list = [
'badRequest',
'conflict',
'forbidden',
'notFound',
'unauthorized'
]);

if(opts.lastRoute == undefined || opts.lastRoute == true) {
app.use(function(req, res, next){
next(ex.NotFound);
});
}

app.use(function(err, req, res, next){
if(!err.name || err.name == 'Error' || !ex.hasOwnProperty(err.name)){
opts.logger(err);

if(req.xhr || opts.plain)
return res.send({ error: 'Internal error' }, 500);

return res.render('errors/500', {
layout: opts.layout,
status: 500,
error: err,
showStack: app.settings.showStackError,
title: 'Oops! Something went wrong!'
});
}
this._logger = config.logger || this._defaultLogger;
this._config = config;

if(req.xhr) return res.send({ error: err.message }, err.status);

if(opts.plain){
res.send(err.message, err.status);
} else {
res.render('errors/' + err.status, {
layout: opts.layout,
status: err.status,
error: err,
showStack: app.settings.showStackError,
title: err.message
});
}
this._app = app;

});
this._errors = { };
this._loadErrors(config.list);

return ex;
this._bind();
}

//
// Loading errors
//

var httpErrors = __dirname + '/http';

fs.readdir(httpErrors, function(err, files){
if(err) throw new Error(err);

files.forEach(function(file){
if(file.charAt(0) == '.') return;
Errors.prototype.get = function(key) {
return key?
this._errors[key] :
this._errors;
};

Errors.prototype.set = function(key, decl) {
this._errors[key] = this._create(decl);
return this;
};

Errors.prototype.unset = function(key) {
delete this._errors[key];
return this;
};

Errors.prototype._create = function(decl) {
var Fn = function() {
Object.keys(decl).forEach(function(key) {
this[key] = decl[key];
}, this);

Error.call(this, this.message);
Error.captureStackTrace(this, Fn);
};

Fn.prototype = Error.prototype;

return Fn;
};

Errors.prototype._loadErrors = function(list) {
list.forEach(function(item) {
var decl;

try {
decl = require(pjoin(ERRORS_DIR, item));
} catch(e) {
if (e.code === 'MODULE_NOT_FOUND') {
decl = require(item);
} else {
throw e;
}
}

this.set(decl.name, decl);
}, this);
};

Errors.prototype._defaultLogger = {
error: function(err) {
console.error(new Date().toLocaleString(), '>>', err);
console.log(err.stack);
}
};

Errors.prototype._bind = function() {
var errors = this.get(),
config = this._config,
logger = this._logger,
app = this._app;

app.use(function(err, req, res, next) {
if(!err.name || err.name == 'Error' || !hasOwn.call(errors, err.name)) {
logger.error(err);
res.status(500);

if(req.xhr || config.plain)
return res.send({ error: 'Internal error' });

return res.render('errors/500', {
layout: config.layout,
error: err,
showStack: app.settings.showStackError,
title: 'Oops! Something went wrong!'
});
}

res.status(err.status);

if(req.xhr) return res.send({ error: err.message });

config.plain?
res.send(err.message) :
res.render((config.errorViews || 'errors') + '/' + err.status, {
layout: config.layout,
error: err,
showStack: app.settings.showStackError,
title: err.message
});

var opts = require(httpErrors + '/' + file);
opts.name = basename(file, '.json');
ex.define(opts);
});
});
});
};

module.exports = ex;
module.exports = Errors;
17 changes: 12 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"author": "Corpix <me@corpix.ru> (http://corpix.ru)",
"author": "Dmitry Moscowski <me@corpix.ru> (http://corpix.ru)",
"name": "express-errors",
"description": "Error handler",
"version": "0.2.2",
"description": "Error handler middleware for express",
"version": "0.3.0",
"repository": {
"type": "git",
"url": "git://github.com/corpix/express-errors.git"
Expand All @@ -11,7 +11,14 @@
"node": ">=0.5.0"
},
"dependencies": {
"express": ">2.x"
"express": ">=3.x"
},
"devDependencies": {}
"devDependencies": {
"mocha": "~1.12.0",
"request": "~2.22.0"
},
"scripts": {
"prepublish": "npm prune",
"test": "./node_modules/.bin/mocha"
}
}
52 changes: 1 addition & 51 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,7 @@
[![build status](https://secure.travis-ci.org/corpix/express-errors.png)](http://travis-ci.org/corpix/express-errors)
##### Express errors

Available in NPM `npm install express-errors`

Provides easy access to common http errors for express e.g.

* 400 - Bad request
* 401 - Unathorized
* 403 - Forbidden
* 404 - NotFound
* 409 - Conflict
* 500 - Internal server error

---
#### Example
You need create `views/errors` directory and add 401,403,404 error views.

```javascript
var logger;
app.get('/400', function(req, res, next){
next(errors.BadRequest); // You will get "bad request" error
});

app.get('/500', function(req, res, next){
next(new Error('Something went wrong :(')); // You will get "Internal server error" error
});

var errors = require('express-errors');

logger = function(err){ // Custom logger function
console.error(err);
}
errors.bind(app, { layout: false, logger: logger }); // IMPORTANT! Call it after all routes ready
```

#### Options
Currently this options are available:

* `lastRoute` - if false `express-errors` will not maintain last app.use route(NotFound error)
* `plain` - if app.settings['view engine'] is undefined OR this option is presented res.send instead of res.render will be used
* `logger` - custom logger function

#### Defining an error
Yeah, you can. Use .define method:

```javascript
errors.define({
name: 'BadRequest', // You will be able to access it through `errors.BadRequest` in future
message: 'Bad request', // This message for XHR requests
status: 400 // HTTP status
});
```

#### TODO

* More options
Look at the tests for docs until I will find time to write something here ;)
Loading

0 comments on commit 936e9be

Please sign in to comment.