自定义错误和bluebird对ErrorClass的捕获导致无意中的行为

时间:2015-11-19 21:31:38

标签: javascript node.js error-handling prototype bluebird

我正在尝试为自定义错误实现模块。

应该可以使用此模块在应用程序的require语句中实例化单个错误:

var MyCustomError = require('custom-error')('MyCustomError');

这是模块:

'use strict';

var _CACHE = {};

function initError(name) {
  function CustomError(message) {
    this.name = name;
    this.message = message;
  }
  CustomError.prototype = Object.create(Error.prototype);
  CustomError.prototype.constructor = CustomError;
  _CACHE[name] = CustomError;
}

function createCustomError(name) {
  if (!_CACHE[name]) {
    initError(name);
  }
  return _CACHE[name];
}

module.exports = createCustomError;

上面的要求一线工作到目前为止。

现在,在我的服务中,我想明确地捕捉到这个错误:

var MyCustomError = require('custom-error')('MyCustomError')
// ...
return fooService.bar()
    .catch(MyCustomError, function (error) {
      logger.warn(error);
      throw error;
    })

如果我在测试中通过抛出MyCustomError来拒绝fooService.bar的承诺,那么这很有效。

但是,这只能起作用,因为我的测试和服务正在使用MyCustomError的相同实例

例如,如果我删除了我的自定义错误模块中的缓存机制,则不再能够达到/执行catch,因为bluebird不理解这两个错误的类型相同:

function createCustomError(name) {
  //if (!_CACHE[name]) {
    initError(name);
  //}
  return _CACHE[name];
}

bluebird的处理的具体代码位于catch_filter.js中,您可以查看right here

虽然这种方法在我的应用程序中运行,但是一旦多个模块使用自定义错误模块并且不再提供相同实例的共享,这将很快导致问题。

如何通过比较实例来启动和运行此概念,但错误类型本身?

干杯,
克里斯托弗

2 个答案:

答案 0 :(得分:3)

我终于提出了一种略有不同的方法。对于志同道合的人来说,这就是结果:

ErrorFactory

var
  vsprintf = require("sprintf-js").vsprintf;

function CustomErrorFactory(code, name, httpCode, message) {

  // Bluebird catcher
  this.predicate = function (it) {
    return it.code === code;
  };

  this.new = function (messageParameters, details) {
    return new CustomError(messageParameters, details);
  };

  this.throw = function (messageParameters, details) {
    throw new CustomError(messageParameters, details);
  }; 

  function CustomError(messageParameters, details) {
    this.code = code;
    this.name = name;
    this.message = vsprintf(message, messageParameters);
    this.httpCode = httpCode;
    this.details = details || {};

    // Important: Do not swallow the stacktrace that lead to here.
    // @See http://stackoverflow.com/questions/8802845/inheriting-from-the-error-object-where-is-the-message-property
    Error.captureStackTrace(this, CustomError);
  }

  // CustomError must be instance of the Error-Object
  CustomError.prototype = Object.create(Error.prototype);
  CustomError.prototype.constructor = CustomError;
}

module.exports = CustomErrorFactory;

错误

var
  ErrorFactory = require("./ErrorFactory");

function initErrors() {
  return {
    Parameter: {
      Missing: new ErrorFactory('1x100', 'ParameterMissing', 400, 'Parameter "%s" missing'),
      Invalid: new ErrorFactory('1x200', 'ParameterInvalid', 400, 'Parameter "%s" invalid')
      //..
    },
    Access: {
      NotAccessible: new ErrorFactory('3x100', 'AccessNotAccessible', 403, 'Resource "%s" is not accessible for "%s"'),
      //..
    },
    // ...
    Request: {
      //..
    }
  };
}

module.exports = initErrors();

我创建了一个包含这些类的单独模块。

然后,在我的实现中,我可以单独捕获这样的错误:

function foo(request, reply) {

  return bluebird
    .resolve(bar)
    .then(reply)

    .catch(Errors.Parameter.Missing.predicate, function () {
      return reply(boom.badRequest());
    })

    .catch(Errors.Entity.NotFound.predicate, function () {
      return reply({}).code(204);
    })

    .catch(Errors.Entity.IllegalState.predicate, function (error) {
      return reply(boom.badImplementation(error.message));
    })

    // any other error
    .catch(function (error) {
      return reply(boom.badImplementation(error.message));
    });
}

投掷

Errors.Entity.IllegalState.throw(['foo', 'bar']);
// or
throw Errors.Entity.IllegalState.new(['foo', 'bar']);

需要

Errors = require('errors'); // all
EntityErors = require('errors').Entity; // one group
EntityNotFoundError = require('errors').Entity.NotFound; // one particular

我仍然不理解的唯一事情是为什么需要使用谓词函数而不是仅仅将错误对象传递给catch-clause。但我可以用那种生活。

答案 1 :(得分:3)

你也可以使用带有catch的谓词函数

function isMyCustomError(f) {
    return f instanceof Error && f.name === "MyCustomError";
}