【问题标题】:What's the best way to expose and compose errors in Node.js?在 Node.js 中公开和编写错误的最佳方法是什么?
【发布时间】:2013-12-12 02:56:12
【问题描述】:

我正在编写一个与 REST API 对话的模块,并且由于 REST API 提供了良好的语义错误响应(例如 403 与 503),我想将这些语义错误传达给调用者。

(编辑:我的意思是,调用者应该能够以编程方式理解错误的原因并采取相应的行动,例如显示适当的 UI。)

对我来说最好的方法是什么?

  1. 为这些语义创建我自己的 Error 子类,例如mymodule.ForbiddenError, mymodule.ServiceUnavailableError?然后调用者将检查instanceof 以获取语义。这在 C# 和 Java 等静态类型语言中最为典型。

  2. 添加例如标准Error 实例的mymoduleCode 属性,带有'Forbidden''ServiceUnavailable' 等语义字符串。 Node.js 本身就是这样做的,例如code: 'ECONNREFUSED'

  3. 其他方式?

==

我现在正在编写另一个模块,它包装了第一个模块。我不想直接暴露内部模块的错误,但是为了可调试性,组合/包装它们会很好。

对我来说最好的方法是什么?

  1. 添加例如我的Error 实例的internalError 属性引用了内部模块的Error 实例。 C# 和 Java 再次这样做。 (Exception#InnerException/Throwable#cause)

  2. 其他方式?

不过,我见过的大多数工具只显示Error 实例的stack 属性,因此在这些情况下这些数据会丢失。是否存在已经存在的典型/常规方式?

【问题讨论】:

    标签: javascript node.js error-handling


    【解决方案1】:

    发现这篇很棒的文章讨论了这个问题:

    http://www.joyent.com/developers/node/design/errors

    此处粘贴过多,但在“具体建议”下,第 2 至第 5 项解决了这个问题:

    1. 清楚你的函数是做什么的。
    2. 对所有错误使用Error 对象(或子类),并实现Error 契约。
    3. 使用错误的name 属性以编程方式区分错误。
    4. 使用解释细节的属性扩充 Error 对象
    5. 如果您将较低级别的错误传递给调用者,请考虑将其包装起来。

    特别是,该文章链接到此模块以包装/组合错误:

    https://github.com/joyent/node-verror

    我不确定我是否有必要遵循这些确切约定——例如我认为拥有应用程序域 code 属性的价值——但这些原则非常有用。

    【讨论】:

    • node-verror 很棒,但有一个致命的 IMO 问题 - 尝试打印空 / 未定义字符串时崩溃。你可以试试这个解决这个问题的叉子:github.com/naddison36/node-verror
    • 有人知道#2 中的“impelement the Error contract”是什么意思吗?
    • 我假设它指的是设置namemessagestack(通过Error.captureStackTrace)。
    【解决方案2】:

    我将简单地将httpCode 属性附加到错误对象。 KISS

    【讨论】:

    • 这在我的示例中可能听起来很合理,但在这种情况下假设 REST API 是一个实现细节。例如。如果您使用模块与服务 X 通信,则无需了解 X 的 REST API 的详细信息,例如它的各种状态代码。
    【解决方案3】:

    您描述的用例是目前 Node 服务不足的用例。由于使用了一些约定,它们往往是以下几种的混合:

    1. 创建简单地从 Error 构造函数派生的新错误类型。主要用于创建高级别的错误类别。您可以使用instanceof 来区分错误类型,但这感觉不是很 JavaScript。
    2. 创建将另一个错误作为构造函数参数然后包装它们的 Java 样式错误子类型。如果您让子类型公开一个 API,那么您基本上是在使用多态性而不是直接分派来处理基于类型的错误。
    3. 通过 Error 构造函数创建语义上有用的错误消息。这是我经常使用的模式。
    4. 将属性附加到错误,这也非常常见。
    5. 创建一个解耦的错误处理 API,可以从错误发生的任何地方调用该 API。这为您提供了一个集中所有代码的地方,这些代码可以确定您有什么样的错误以及您应该如何处理它,并注入您处理错误所需的任何依赖项。

    这里真正的问题是这些都是临时机制,我不会说它们中的任何一个都达到了临界质量。我认为这部分是由于异步性和 JavaScript 的结合使得正确和完全处理错误变得困难,因此建议通常是在捕获有关错误的信息后关闭整个过程。

    这并不能很好地处理预期的错误,但我认为大多数错误要么被框架捕获(例如 Express 错误处理程序),要么被放入 Node 回调约定中。简而言之,我认为这里有定义一些约定的空间,因为最先进的技术并不是那么巧妙。

    【讨论】:

    • 感谢您的精彩总结。我可以建议将“aseemSays”属性添加为标准吗? :-)
    • 感谢福雷斯特,同意很好的总结。你是对的,大多数时候错误不是/不需要进行语义分析,但我遇到了几个我这样做的情况。例如。我调用一个函数来注册一个新用户,它会抛出一个错误。那是 db 坏了吗?或者 bc “唯一电子邮件”约束失败?我想在这两种情况下采取不同的行动。听起来像附加属性是你在那里做的?
    【解决方案4】:

    我对 node 也很陌生,所以对它持保留态度,但我会选择你的 #2 选项,添加一个属性。 (This is also suggested here).

    这几乎是您最初的#2 选项。如果错误处理程序只记录堆栈,而不是错误本身,那是他们的错。 :-)(说真的,不知道你怎么能做得更好)

    【讨论】:

    • 感谢您的链接!你认为你会如何编写错误呢?只需使用您自己的属性(y/ies)扩展原始实例?
    • 是的。我会添加一个属性,命名为“semanticDetails”或“aseemSays”。 :-) 它可以是一个字符串,也可以是具有多个属性的 { } 本身:'.moduleName'、'.errorCode' 等...记录它以便收到错误的程序员知道在哪里查找。
    【解决方案5】:

    编辑我是 StackOverflow Node.js 的新手。其他人应该回答这个问题! :-)

    如果您来自 Java/C#,欢迎使用 JavaScript!我从未见过 Node.js 库使用 instanceof 来检查错误类型,但正如您所说,这在静态语言中很常见。

    话虽如此,通常只创建新错误,并使用 NodeJS 的回调样式(err, response) 进行回调。

    我总是遇到来自其他模块的错误消息,了解它们的来源与创建可能隐藏在我身上的实际死亡位置的包装错误消息(需要我更多地挖掘)是有帮助的。

    创建一个可以处理字符串(或在您的情况下为无效响应)的其余错误消息的函数的示例:

    function myFunction(callback) {
        callRestAPI('http://someApi.com/request', function(errorString, jsonResponse) {
            if (errorString) { 
                callback(new Error("something wrong in myFunction! " + errorString)); 
            } else {
                callback(null, JSON.parse(jsonResponse));
            }
        });
    }
    

    在您的情况下,“errorString”检查基本上是响应(403 / 503),您可以构建您的错误消息以发送到new Error( ... ),例如new Error('Failed! Got response 403!')

    我可能错过了你的意思,也许其他人可以更彻底。


    重新阅读后,您可以发布您正在包装的模块吗?是节点请求吗?

    【讨论】:

    • 嗨 Glareo,感谢您的回复!我对 Node 并不陌生,但我很感谢您的欢迎。 =)我的问题本来可以更清楚的:当我说“语义”时,我的意思是揭露一个事实,例如这是客户端权限错误与服务器过载错误;所以它不应该在字符串消息中。节点核心在其错误中添加了一个code 属性;这让调用者可以区分错误原因/类型。但这并不能很好地适用于可组合性。这有意义吗?
    • 我一般地问这个问题,但我希望将它应用到的一个具体案例是我维护的这个数据库驱动程序模块:npmjs.org/package/neo4j 我将它包装在一个业务逻辑模块中我正在为一个应用程序写作。目前,这个 db 模块只抛出非语义标准 Error 带有字符串消息的实例;我想改变它。
    • 我可能是回答这个问题的最糟糕的人。我对 Node.JS 很陌生。祝你好运:D
    • 没问题。 =) 感谢您抽出宝贵时间提供帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-20
    • 2017-12-28
    • 2017-07-30
    相关资源
    最近更新 更多