123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- 'use strict';
- const http = require('http');
- const fs = require('fs');
- const onerror = require('koa-onerror');
- const ErrorView = require('./lib/error_view');
- const {
- isProd,
- detectStatus,
- detectErrorMessage,
- accepts,
- } = require('./lib/utils');
- module.exports = app => {
- // logging error
- const config = app.config.onerror;
- const viewTemplate = fs.readFileSync(config.templatePath, 'utf8');
- app.on('error', (err, ctx) => {
- ctx = ctx || app.createAnonymousContext();
- if (config.appErrorFilter && !config.appErrorFilter(err, ctx)) return;
- const status = detectStatus(err);
- // 5xx
- if (status >= 500) {
- try {
- ctx.logger.error(err);
- } catch (ex) {
- app.logger.error(err);
- app.logger.error(ex);
- }
- return;
- }
- // 4xx
- try {
- ctx.logger.warn(err);
- } catch (ex) {
- app.logger.warn(err);
- app.logger.error(ex);
- }
- });
- const errorOptions = {
- // support customize accepts function
- accepts() {
- const fn = config.accepts || accepts;
- return fn(this);
- },
- html(err) {
- const status = detectStatus(err);
- const errorPageUrl = typeof config.errorPageUrl === 'function'
- ? config.errorPageUrl(err, this)
- : config.errorPageUrl;
- // keep the real response status
- this.realStatus = status;
- // don't respond any error message in production env
- if (isProd(app)) {
- // 5xx
- if (status >= 500) {
- if (errorPageUrl) {
- const statusQuery =
- (errorPageUrl.indexOf('?') > 0 ? '&' : '?') +
- `real_status=${status}`;
- return this.redirect(errorPageUrl + statusQuery);
- }
- this.status = 500;
- this.body = `<h2>Internal Server Error, real status: ${status}</h2>`;
- return;
- }
- // 4xx
- this.status = status;
- this.body = `<h2>${status} ${http.STATUS_CODES[status]}</h2>`;
- return;
- }
- // show simple error format for unittest
- if (app.config.env === 'unittest') {
- this.status = status;
- this.body = `${err.name}: ${err.message}\n${err.stack}`;
- return;
- }
- const errorView = new ErrorView(this, err, viewTemplate);
- this.body = errorView.toHTML();
- },
- json(err) {
- const status = detectStatus(err);
- let errorJson = {};
- this.status = status;
- const code = err.code || err.type;
- const message = detectErrorMessage(this, err);
- if (isProd(app)) {
- // 5xx server side error
- if (status >= 500) {
- errorJson = {
- code,
- // don't respond any error message in production env
- message: http.STATUS_CODES[status],
- };
- } else {
- // 4xx client side error
- // addition `errors`
- errorJson = {
- code,
- message,
- errors: err.errors,
- };
- }
- } else {
- errorJson = {
- code,
- message,
- errors: err.errors,
- };
- if (status >= 500) {
- // provide detail error stack in local env
- errorJson.stack = err.stack;
- errorJson.name = err.name;
- for (const key in err) {
- if (!errorJson[key]) {
- errorJson[key] = err[key];
- }
- }
- }
- }
- this.body = errorJson;
- },
- js(err) {
- errorOptions.json.call(this, err, this);
- if (this.createJsonpBody) {
- this.createJsonpBody(this.body);
- }
- },
- };
- // support customize error response
- [ 'all', 'html', 'json', 'text', 'js' ].forEach(type => {
- if (config[type]) errorOptions[type] = config[type];
- });
- onerror(app, errorOptions);
- };
|