Skip to content

Commit c404788

Browse files
committed
Merge pull request hapijs#207 from walmartlabs/user/eran
Error refactor, custom error support (passThrough)
2 parents b66ca05 + 44d8526 commit c404788

File tree

8 files changed

+296
-144
lines changed

8 files changed

+296
-144
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,5 @@ config.json
1414
*/._*
1515
*/*/._*
1616
coverage.*
17+
lib-cov
18+

Readme.md

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -249,15 +249,15 @@ function onUnknownRoute(request) {
249249
If a different error format than the default JSON response is required, the server `errors.format` option can be assigned a function to generate a
250250
different error response. The function signature is _'function (result, callback)'_ where:
251251
- _'result'_ - is the **hapi** error object returned by the route handler, and
252-
- _'callback'_ - is the callback function called with the new result object or string.
252+
- _'callback'_ - is a callback function called with the formatted response. The callback function signature is _'function (code, payload, contentType)'_.
253253

254254
For example:
255255
```javascript
256256
var options = {
257257
errors: {
258258
format: function (result, callback) {
259259

260-
callback('Oops: ' + result.message);
260+
callback(500, 'Oops: ' + result.message, 'text/html');
261261
}
262262
}
263263
};
@@ -701,12 +701,11 @@ In which:
701701
## Response Errors
702702

703703
The 'Hapi.Error' module provides helper methods to generate error responses:
704-
- _'badRequest([message])'_ - HTTP 400 (Bad request).
704+
- _'badRequest([message])'_ - HTTP 400 (Bad Request).
705705
- _'unauthorized([message])'_ - HTTP 401 (Unauthorized).
706-
- _'forbidden([message])'_ - HTTP 403 (Not allowed).
707-
- _'notFound([message])'_ - HTTP 404 (Not found).
708-
- _'internal([message, data])'_ - HTTP 500 (Internal error). The optional _message_ and _data_ values are not returned to the client but are logged internally.
709-
- _'create(message, code, text, [options]) - creates a custom error with the provided _message_, _code_ (the HTTP status code), _text_ (the HTTP status message), and any keys present in _options_.
706+
- _'forbidden([message])'_ - HTTP 403 (Not Allowed).
707+
- _'notFound([message])'_ - HTTP 404 (Not Found).
708+
- _'internal([message, data])'_ - HTTP 500 (Internal Error). The optional _message_ and _data_ values are not returned to the client but are logged internally.
710709

711710
The _message_ value is optional and will be returned to the client in the response unless noted otherwise. For example:
712711

@@ -719,7 +718,7 @@ function onUnknownRoute(request) {
719718

720719
Error responses are send as JSON payload with the following keys (unless an [error response override](#errors) is configured):
721720
- _code_ - the HTTP status code (e.g. 400).
722-
- _error_ - the HTTP status message (e.g. 'Bad request').
721+
- _error_ - the HTTP status message (e.g. 'Bad Request').
723722
- _message_ - the returned message if provided.
724723

725724
The complete error repsonse including any additional data is added to the request log.

lib/error.js

Lines changed: 109 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// Load modules
22

3+
var Http = require('http');
4+
var NodeUtil = require('util');
35
var Utils = require('./utils');
46

57

@@ -8,78 +10,151 @@ var Utils = require('./utils');
810
var internals = {};
911

1012

11-
exports.badRequest = function (message) {
13+
exports = module.exports = internals.Error = function (code, message, options) {
1214

13-
return exports.create(message, 400, 'Bad request');
15+
Utils.assert(this.constructor === internals.Error, 'Error must be instantiated using new');
16+
Utils.assert(!options || !options.toResponse || typeof options.toResponse === 'function', 'options.toReponse must be a function');
17+
Utils.assert(code >= 400 && code < 600, 'Error code must be 4xx or 5xx');
18+
19+
Error.call(this);
20+
21+
this.name = 'HapiError';
22+
this.code = code;
23+
this.message = message;
24+
this.settings = Utils.clone(options) || {}; // Options can be reused;
25+
26+
return this;
1427
};
1528

29+
NodeUtil.inherits(internals.Error, Error);
30+
31+
32+
internals.Error.prototype.toResponse = function () {
33+
34+
if (this.settings.toResponse) {
35+
36+
return this.settings.toResponse.call(this);
37+
}
38+
39+
var response = {
40+
code: this.code,
41+
payload: {
42+
error: Http.STATUS_CODES[this.code] || 'Unknown',
43+
code: this.code,
44+
message: this.message
45+
}
46+
// contentType: 'application/json'
47+
};
48+
49+
for (var d in this) {
50+
if (this.hasOwnProperty(d) &&
51+
!response.payload.hasOwnProperty(d)) {
1652

17-
exports.unauthorized = function (message) {
53+
response.payload[d] = this[d];
54+
}
55+
}
1856

19-
return exports.create(message, 401, 'Unauthorized');
57+
return response;
2058
};
2159

2260

23-
exports.forbidden = function (message) {
61+
// Utilities
62+
63+
internals.Error.badRequest = function (message) {
2464

25-
return exports.create(message, 403, 'Not allowed');
65+
return new internals.Error(400, message);
2666
};
2767

2868

29-
exports.notFound = function (message) {
69+
internals.Error.unauthorized = function (message) {
3070

31-
return exports.create(message, 404, 'Not Found');
71+
return new internals.Error(401, message);
3272
};
3373

3474

35-
exports.internal = function (message, data) {
75+
internals.Error.forbidden = function (message) {
3676

37-
var custom = {
38-
trace: Utils.callStack(1)
39-
};
77+
return new internals.Error(403, message);
78+
};
4079

41-
if (data) {
42-
custom.data = data;
43-
}
4480

45-
return exports.create(message, 500, 'Internal error', custom);
81+
internals.Error.notFound = function (message) {
82+
83+
return new internals.Error(404, message);
4684
};
4785

4886

49-
exports.format = function (error) {
87+
internals.Error.internal = function (message, data) {
5088

51-
if (error.hasOwnProperty('toResponse') &&
52-
typeof error.toResponse === 'function') {
89+
var format = function () {
5390

54-
return error.toResponse();
55-
}
91+
var response = {
92+
code: 500,
93+
payload: {
94+
error: Http.STATUS_CODES[500],
95+
code: 500,
96+
message: 'An internal server error occurred' // Hide actual error from user
97+
}
98+
};
5699

57-
var err = {
58-
error: error.text,
59-
code: error.code,
60-
message: (error.code >= 500 && error.code < 600 ? 'An internal server error occurred' : error.message)
100+
return response;
61101
};
62102

103+
var err = new internals.Error(500, message, { toResponse: format });
104+
err.trace = Utils.callStack(1);
105+
err.data = data;
63106
return err;
64107
};
65108

66109

67-
exports.create = function (message, code, text, options) {
110+
internals.Error.passThrough = function (code, payload, contentType) {
68111

69-
var err = new Error();
70-
err.message = message;
71-
err.code = code;
72-
err.text = text;
112+
var format = function () {
73113

74-
for (var d in options) {
75-
if (options.hasOwnProperty(d)) {
76-
err[d] = options[d];
114+
var response = {
115+
code: code,
116+
payload: payload,
117+
contentType: contentType
118+
};
119+
120+
return response;
121+
};
122+
123+
var err = new internals.Error(code, 'Pass-through', { toResponse: format });
124+
return err;
125+
};
126+
127+
128+
internals.Error.toResponse = function (err) {
129+
130+
Utils.assert(err instanceof Error, 'Input must be instance of Error');
131+
132+
if (err instanceof internals.Error) {
133+
134+
return err.toResponse();
135+
}
136+
137+
// Other Error
138+
139+
var response = {
140+
code: 500,
141+
payload: {
142+
message: err.message,
143+
name: err.name
144+
}
145+
};
146+
147+
for (var d in err) {
148+
if (err.hasOwnProperty(d)) {
149+
response.payload[d] = err[d];
77150
}
78151
}
79152

80-
return err;
153+
return response;
81154
};
82155

83156

84157

85158

159+
160+

0 commit comments

Comments
 (0)