Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added display-errors & display-sections option #33

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion app.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,19 @@ module.exports = app => {
? config.errorPageUrl(err, this)
: config.errorPageUrl;

// If it's null, then use default isProd logic
let displayErrors = config.displayErrors === null ? !isProd(app) : config.displayErrors;

// It also can be a function
if (typeof displayErrors === 'function') {
displayErrors = displayErrors.call(app);
}

// keep the real response status
this.realStatus = status;
// don't respond any error message in production env
if (isProd(app)) {
// if displayErrors set false, or unset, then use default logic
if (!displayErrors) {
// 5xx
if (status >= 500) {
if (errorPageUrl) {
Expand Down
6 changes: 6 additions & 0 deletions config/config.default.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,10 @@ exports.onerror = {
appErrorFilter: null,
// default template path
templatePath: path.join(__dirname, '../lib/onerror_page.mustache'),
// Set displayErrors to true, to display erros.
// If it's not set, then use the default isProd logic for it.
displayErrors: null,
// Sections you want to show in error page.
// Default to show all sections.
displaySections: [ 'CodeFrames', 'Headers', 'Cookies', 'AppInfo' ],
};
58 changes: 36 additions & 22 deletions lib/error_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class ErrorView {
this.app = ctx.app;
this.assets = new Map();
this.viewTemplate = template;
this.displaySections = this.app.config.onerror.displaySections;
}

/**
Expand All @@ -37,8 +38,14 @@ class ErrorView {
});

data.request = this.serializeRequest();
data.appInfo = this.serializeAppInfo();

if (this.displaySections.indexOf('AppInfo') > -1) {
data.appInfo = this.serializeAppInfo();
}

this.displaySections.forEach(item => {
data['display' + item + 'Section'] = true;
});
return this.complieView(this.viewTemplate, data);
}

Expand Down Expand Up @@ -214,13 +221,17 @@ class ErrorView {
if (code) {
message = `${message} (code: ${code})`;
}
return {

const data = {
code,
message,
name: this.error.name,
status: this.error.status,
frames: stack instanceof Array ? stack.filter(frame => frame.getFileName()).map(frameFomatter) : [],
};
if (this.displaySections.indexOf('CodeFrames') > -1) {
data.frames = stack instanceof Array ? stack.filter(frame => frame.getFileName()).map(frameFomatter) : [];
}
return data;
}

/**
Expand All @@ -229,31 +240,34 @@ class ErrorView {
* @return {Object} request object
*/
serializeRequest() {
const headers = [];

Object.keys(this.request.headers).forEach(key => {
if (this._filterHeaders.includes(key)) {
return;
}
headers.push({
key,
value: this.request.headers[key],
});
});

const parsedCookies = cookie.parse(this.request.headers.cookie || '');
const cookies = Object.keys(parsedCookies).map(key => {
return { key, value: parsedCookies[key] };
});

return {
const data = {
url: this.request.url,
httpVersion: this.request.httpVersion,
method: this.request.method,
connection: this.request.headers.connection,
headers,
cookies,
};

if (this.displaySections.indexOf('Headers') > -1) {
const headers = [];
Object.keys(this.request.headers).forEach(key => {
if (this._filterHeaders.includes(key)) {
return;
}
headers.push({
key,
value: this.request.headers[key],
});
});
data.headers = headers;
}
if (this.displaySections.indexOf('Cookies') > -1) {
const parsedCookies = cookie.parse(this.request.headers.cookie || '');
data.cookies = Object.keys(parsedCookies).map(key => {
return { key, value: parsedCookies[key] };
});
}
return data;
}

/**
Expand Down
26 changes: 18 additions & 8 deletions lib/onerror_page.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,7 @@

<img class="error-logo" src="https://zos.alipayobjects.com/rmsportal/JFKAMfmPehWfhBPdCjrw.svg"/>

{{#displayCodeFramesSection}}
<div class="error-frames">
<div class="frame-preview is-hidden">
<div id="frame-file"></div>
Expand All @@ -593,8 +594,8 @@
<label for="frames-filter">Show all frames</label>
</div>

{{#frames}}
<div class="frames-list">
{{#frames}}
{{index}}
<div class="frame-row {{classes}}">
<div class="frame-row-filepath">
Expand All @@ -619,8 +620,8 @@
</div>
</div>
</div>
{{/displayCodeFramesSection}}
</section>

<section class="request-details">
<h2 class="request-title"> Request Details </h2>
<div class="table">
Expand All @@ -645,25 +646,33 @@
</div>
</div>


{{#displayHeadersSection}}
<h2 class="request-title"> Headers </h2>
<div class="table">
{{#request.headers}}
{{#request.headers}}
<div class="tr">
<div class="td title">{{ key }}</div>
<div class="td content">{{ value }}</div>
</div>
{{/request.headers}}
{{/request.headers}}
</div>
{{/displayHeadersSection}}


{{#displayCookiesSection}}
<h2 class="request-title"> Cookies </h2>
<div class="table">
{{#request.cookies}}
{{#request.cookies}}
<div class="tr">
<div class="td title">{{ key }}</div>
<div class="td content">{{ value }}</div>
</div>
{{/request.cookies}}
{{/request.cookies}}
</div>
{{/displayCookiesSection}}

{{#displayAppInfoSection}}
<h2 class="request-title"> AppInfo </h2>
<div class="table">
<div class="tr">
Expand All @@ -676,7 +685,8 @@
<pre class="line-numbers"><code class="language-json">{{ appInfo.config }}</code></pre>
</div>
</div>
</<div>
</div>
{{/displayAppInfoSection}}
</section>

<script type="text/javascript">
Expand Down Expand Up @@ -751,4 +761,4 @@ Prism.languages.typescript=Prism.languages.extend("javascript",{keyword:/\b(as|a
</script>
</section>
</body>
</html>
</html>
7 changes: 7 additions & 0 deletions test/fixtures/onerror-display-error/app/extend/context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

module.exports = {
get userId() {
throw new Error('you can`t get userId.');
},
};
7 changes: 7 additions & 0 deletions test/fixtures/onerror-display-error/app/router.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

module.exports = app => {
app.get('/500', function* () {
this.throw(500, 'hi, this custom 500 page');
});
};
6 changes: 6 additions & 0 deletions test/fixtures/onerror-display-error/config/config.default.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
'use strict';

exports.keys = 'foo,bar';
exports.onerror = {
displayErrors: process.env.SHOW_ERRORS - 1 === 0,
};
3 changes: 3 additions & 0 deletions test/fixtures/onerror-display-error/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "onerror-display-error"
}
64 changes: 64 additions & 0 deletions test/onerror.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,70 @@ describe('test/onerror.test.js', () => {
});
});

describe('onerror.display-error', () => {
let app;
before(() => {
process.env.SHOW_ERRORS = 1;
mm.env('prod');
mm.consoleLevel('NONE');
app = mm.app({
baseDir: 'onerror-display-error',
});
return app.ready();
});
after(() => {
delete process.env.SHOW_ERRORS;
app.close();
});

it('should 500 full html', () => {
return app.httpRequest()
.get('/500')
.expect(500)
.expect(/hi, this custom 500 page/);
});

it('should works as a function', () => {
mm(app.config.onerror, 'displayErrors', () => false);
return app.httpRequest()
.get('/500')
.expect(res => {
assert.equal(res.text.includes('Internal Server Error'), true);
})
.expect(500);
});

it('should no headers in 500 full html', () => {
mm(app.config.onerror, 'displaySections', [ 'CodeFrames', 'Cookies', 'AppInfo' ]);
mm(app.config.onerror, 'displayErrors', true);
return app.httpRequest()
.get('/500')
.expect(res => {
assert.equal(res.text.includes('<h2 class="request-title"> Cookies </h2>'), true);
assert.equal(res.text.includes('<div class="error-frames">'), true);
assert.equal(res.text.includes('<h2 class="request-title"> AppInfo </h2>'), true);
assert.equal(res.text.includes('<h2 class="request-title"> Headers </h2>'), false);
})
.expect(500)
.expect(/hi, this custom 500 page/);
});

it('should only have headers in 500 full html', () => {
mm(app.config.onerror, 'displaySections', [ 'Headers' ]);
mm(app.config.onerror, 'displayErrors', true);
return app.httpRequest()
.get('/500')
.expect(res => {
assert.equal(res.text.includes('<h2 class="request-title"> Cookies </h2>'), false);
assert.equal(res.text.includes('<div class="error-frames">'), false);
assert.equal(res.text.includes('<h2 class="request-title"> AppInfo </h2>'), false);
assert.equal(res.text.includes('<h2 class="request-title"> Headers </h2>'), true);
})
.expect(500)
.expect(/hi, this custom 500 page/);
});
});

describe('appErrorFilter', () => {
let app;
before(() => {
Expand Down
Loading