UNPKG

4.22 kBPlain TextView Raw
1import path from 'path';
2import { NotFound, GeneralError } from '@feathersjs/errors';
3import { Request, Response, NextFunction, ErrorRequestHandler, RequestHandler } from 'express';
4
5const defaults = {
6 public: path.resolve(__dirname, '..', 'public'),
7 logger: console
8};
9const defaultHtmlError = path.resolve(defaults.public, 'default.html');
10
11export function notFound ({ verbose = false } = {}): RequestHandler {
12 return function (req: Request, _res: Response, next: NextFunction) {
13 const url = `${req.url}`;
14 const message = `Page not found${verbose ? ': ' + url : ''}`;
15
16 next(new NotFound(message, { url }));
17 };
18}
19
20export type ErrorHandlerOptions = {
21 public?: string;
22 logger?: boolean|{ error?: (msg: any) => void, info?: (msg: any) => void };
23 html?: any;
24 json?: any;
25};
26
27export function errorHandler (_options: ErrorHandlerOptions = {}): ErrorRequestHandler {
28 const options = Object.assign({}, defaults, _options);
29
30 if (typeof options.html === 'undefined') {
31 options.html = {
32 401: path.resolve(options.public, '401.html'),
33 404: path.resolve(options.public, '404.html'),
34 default: defaultHtmlError
35 };
36 }
37
38 if (typeof options.json === 'undefined') {
39 options.json = {};
40 }
41
42 return function (error: any, req: Request, res: Response, next: NextFunction) {
43 // Set the error code for HTTP processing semantics
44 error.code = !isNaN(parseInt(error.code, 10)) ? parseInt(error.code, 10) : 500;
45
46 // Log the error if it didn't come from a service method call
47 if (options.logger && typeof options.logger.error === 'function' && !res.hook) {
48 if (error.code >= 500) {
49 options.logger.error(error);
50 } else {
51 options.logger.info(error);
52 }
53 }
54
55 if (error.type !== 'FeathersError') {
56 const oldError = error;
57
58 error = oldError.errors ? new GeneralError(oldError.message, {
59 errors: oldError.errors
60 }) : new GeneralError(oldError.message);
61
62 if (oldError.stack) {
63 error.stack = oldError.stack;
64 }
65 }
66
67 const formatter: { [key: string]: any } = {};
68
69 // If the developer passed a custom function for ALL html errors
70 if (typeof options.html === 'function') {
71 formatter['text/html'] = options.html;
72 } else {
73 let file = options.html[error.code];
74 if (!file) {
75 file = options.html.default || defaultHtmlError;
76 }
77 // If the developer passed a custom function for individual html errors
78 if (typeof file === 'function') {
79 formatter['text/html'] = file;
80 } else {
81 formatter['text/html'] = function () {
82 res.set('Content-Type', 'text/html');
83 res.sendFile(file);
84 };
85 }
86 }
87
88 // If the developer passed a custom function for ALL json errors
89 if (typeof options.json === 'function') {
90 formatter['application/json'] = options.json;
91 } else {
92 const handler = options.json[error.code] || options.json.default;
93 // If the developer passed a custom function for individual json errors
94 if (typeof handler === 'function') {
95 formatter['application/json'] = handler;
96 } else {
97 // Don't show stack trace if it is a 404 error
98 if (error.code === 404) {
99 error.stack = null;
100 }
101
102 formatter['application/json'] = function () {
103 const output = Object.assign({}, error.toJSON());
104
105 if (process.env.NODE_ENV === 'production') {
106 delete output.stack;
107 }
108
109 res.set('Content-Type', 'application/json');
110 res.json(output);
111 };
112 }
113 }
114
115 res.status(error.code);
116
117 const contentType = req.headers['content-type'] || '';
118 const accepts = req.headers.accept || '';
119
120 // by default just send back json
121 if (contentType.indexOf('json') !== -1 || accepts.indexOf('json') !== -1) {
122 formatter['application/json'](error, req, res, next);
123 } else if (options.html && (contentType.indexOf('html') !== -1 || accepts.indexOf('html') !== -1)) {
124 formatter['text/html'](error, req, res, next);
125 } else {
126 // TODO (EK): Maybe just return plain text
127 formatter['application/json'](error, req, res, next);
128 }
129 };
130}