moleculer-api
Version:
A dynamic API Gateway for MoleculerJS which updates REST endpoints and aggregated GraphQL schema, access control policy for each action calls from metadata of remote services schema without restart or deployment.
98 lines • 3.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ErrorMiddleware = void 0;
const tslib_1 = require("tslib");
const _ = tslib_1.__importStar(require("lodash"));
const middleware_1 = require("./middleware");
/*
Uncaught Error handling middleware
*/
class ErrorMiddleware extends middleware_1.ServerMiddleware {
constructor(props, opts) {
super(props);
this.props = props;
this.opts = _.defaultsDeep(opts || {}, ErrorMiddleware.autoLoadOptions);
}
apply(modules) {
/* HTTP Error & Not Found handling */
const httpNotFoundHandler = this.handleHTTPNotFound.bind(this);
const httpErrorHandler = this.handleHTTPError.bind(this);
// arrange error handler to the last or stack on any sub app mounts
// ref: http://expressjs.com/en/guide/error-handling.html
const arrangeHTTPErrorHandlers = () => {
const layers = modules.http._router.stack;
// not found handler should be the last-1 layer
const notFoundHandlerIndex = layers.findIndex((layer) => layer.handle === httpNotFoundHandler);
console.assert(notFoundHandlerIndex !== -1, "where the http not found handler gone?");
layers.push(...layers.splice(notFoundHandlerIndex, 1));
// error handler should be the last layer
const errorHandlerIndex = layers.findIndex((layer) => layer.handle === httpErrorHandler);
console.assert(errorHandlerIndex !== -1, "where the http error handler gone?");
layers.push(...layers.splice(errorHandlerIndex, 1));
};
// mount handlers
modules.http.use(httpNotFoundHandler);
modules.http.use(httpErrorHandler);
arrangeHTTPErrorHandlers();
modules.http.on("update", arrangeHTTPErrorHandlers);
/* WebSocket Server Error handling */
modules.ws.on("error", this.handleWebSocketError.bind(this));
}
handleHTTPError(error, req, res, next) {
this.props.logger.error(error);
if (res.headersSent) {
return next(error);
}
const err = this.formatError(error);
let status = typeof err !== "string" && err.error && (err.error.status || err.error.statusCode || err.error.code) || 500;
if (isNaN(status))
status = 500;
res.status(status).json(err); // TODO: normalize error
}
handleHTTPNotFound(req, res, next) {
res.status(404).end();
}
handleWebSocketError(error, socket, req) {
if (socket) {
socket.send(this.formatError(error, true)); // TODO: normalize error
}
}
formatError(error, stringify = false) {
const { responseFormat, displayErrorStack } = this.opts;
let value = error;
if (typeof error === "object" && error !== null) {
const obj = {};
for (const key of Object.getOwnPropertyNames(error)) {
if (key !== "stack" || displayErrorStack) {
obj[key] = error[key];
}
}
value = obj;
}
if (responseFormat) {
try {
value = responseFormat(value);
}
catch (e) {
this.props.logger.error("failed to format error", e);
}
}
let result = { error: value };
if (stringify) {
try {
result = JSON.stringify(result);
}
catch (e) {
console.error("failed to stringify error", e);
result = JSON.stringify({ error: error.toString(), truncated: true });
}
}
return result;
}
}
exports.ErrorMiddleware = ErrorMiddleware;
ErrorMiddleware.key = "error";
ErrorMiddleware.autoLoadOptions = {
displayErrorStack: true,
};
//# sourceMappingURL=error.js.map