需要一种方法来系统地处理NodeJS项目中的错误/异常/拒绝

时间:2016-03-27 17:40:14

标签: javascript node.js error-handling exception-handling

背景资料

我正在开发一个新项目,它将提供可以安装的多个不同(可选)软件包,所有这些软件包都是核心软件包(仅手动软件包)的补充。其他包只与核心进行交互。

该项目只是为了跟踪数据列表(我知道不是非常具体,但不需要这些细节)。附加软件包确定数据列表如何与之交互。 核心包只包含所有主要的JS功能和数据库模型以及身份验证。其他包与这些相关。

  • 假设您希望将其作为标准网页,您可以安装 webui 软件包,该软件包将绑定到核心,并创建一个Web应用程序为它
  • 如果要创建API,可以安装 restapi 包,从而创建RESTful接口;您还可以安装 spaui 包,该包将与RESTful界面进行交互,后者从核心
  • 获取数据

这些插件包我称之为“facade”包。您真正需要从上面推断的是核心是与 facade 包相对应的独立包,它处理核心功能(数据库内容,身份验证,授权等)

问题

核心可以使用promises或callbacks,并返回失败的异常,然后用于与核心交互的任何facade包将处理异常/错误(显示HTTP错误页面,返回RESTful错误结果等。)

由于处理错误的包与返回错误的包不同,因此需要系统地了解返回错误的 type ,因此可以正确处理(EG:webui / restui包应该知道它是否需要显示HTTP 500,HTTP 403,HTTP 409等)。显然核心只返回new Error('Something broke'),然后外观包并不真正知道它是什么类型的错误,除非他们将文本保存在某处并且可以将其与错误代码匹配。

问题

最好的方法是什么?我找不到任何可以完全符合我想要的东西..

我最终开始自己的尝试..(下)

我的可能的解决方案(如果这已足够,只需确认)

我创建了一个新的AppError异常类型,而不是使用简单字符串返回AppError个异常,而是提供错误代码,它会将该异常与错误消息相关联,错误类型等。

以下是AppError例外的示例用法:

exports.createThing = ( name, data ) => {
    return new Promise( ( res, rej ) => {
        if( doesItExist( name ) )
            return rej( new AppError( 'document.create.duplicateName' ) )

        // Other stuff...
    })
}

现在在AppError异常方法中,它需要code并查看异常列表(code应该是异常数据对象中的键)。

以下是上述异常的异常数据对象包含的示例:

module.exports = {
    'document.create.duplicateName': {
        type: 'DocumentConflict',
        message: 'Failed to create new document',
        detail: 'The document name specified already exists, try another one'
    }
}

使用示例:假设我们尝试使用已存在的createThing(来自name包中)执行webui

CorePackage.createThing( 'foobar', 'some data' )
    .catch( err => {
        /*
        The err is now an instance of AppError

        err.type -> DocumentConflict
        err.message -> Failed to create new document
        err.detail -> The document name specified already exists, try another one
        */
    })

从这里开始,就像将err.type值与合适的HTTP错误代码相关联一样简单! (可能是HTTP 409 Conflict)。显然,这些关联可以保存在一个对象中,这样可以很容易地为返回的任何错误type值检索正确的错误代码。然后,错误代码的文本就在err.messageerr.detail

这也使得在应用程序中引入某种类型的语言环境变得容易,因为错误,因为所有需要做的就是编辑异常数据对象。

发布结束

因此,如果您认为我的解决方案是充分的,并且您无法想到任何问题,那么请说出来。我想知道它是否或不是。即使你不能想出一个合适的解决方案,但你只知道我创建的那个不会工作,也要分享它。

如果您有替代解决方案,那么这也会起作用!

由于

1 个答案:

答案 0 :(得分:1)

我认为有两种基本方法可以解决这个问题:

  • code属性:创建一个新的\Error对象,并为代码属性分配有关错误的信息。例如:

    var err = new Error('Message');
    err.code = "DocumentConflict";
    
  • 自定义错误对象。您可以为每种错误类型分配一个单独的Error对象。例如,您可能会遇到AppError错误,而不仅仅是DocumentConflict

对于我正在创建RESTful API的项目,我喜欢考虑错误代码。对于大多数项目,端点将返回以下代码之一:

  • 400(错误请求)
  • 401(凭据错误)
  • 403(禁止)
  • 404(未找到)。
  • 500(内部服务器错误)。

然后,这些将成为我在应用程序中传递的“标准”错误类型。普通Error对象被解释为内部服务器错误,因此总是将500传递给端点。

例如,

CredentialsError = function (message) {
  Error.call(this, arguments);
  Error.captureStackTrace(this, this.constructor);
  this.message = message;
};

util.inherits(CredentialsError, Error);
CredentialsError.prototype.name = "CredentialsError";

然后根据需要返回/抛出一个new CredentialsError("Invalid password")对象。要检查对象的类型,可以使用instanceof。例如,使用Express,您可以使用类似于以下内容的错误处理程序:

app.use(function(err, req, res, next) {
  var status;

  if (err instanceof error.FieldError) {
    status = 400;
  } else if (err instanceof error.CredentialsError) {
    status = 401;
  /* etc */
  } else {
    status = 500;
  }

  if (status !== 500) {
    res.status(status).send(JSON.stringify(
      err,
      null,
      4
    ));
  } else {
    // for 500, do not output the error!
    console.error(err.stack);
    res.status(500).send({
      message: "Internal Server Error"
    });
  }
});

值得注意的是,您可以定义自定义错误对象构造函数,而不仅仅是字符串。例如,您可以将对象传递给BadRequestError构造函数以提供字段级错误详细信息。

现在,在大多数情况下,您可以传播错误,并且对端点的响应也是有意义的。但是,有些情况下您希望转换错误类型。例如,如果您有登录端点,则可以向findUserByEmailAddress()发出请求。这可能会返回NotFoundError对象,但您希望在signIn()函数中捕获它并将其转换为CredentialsError。