【问题标题】:Error reporting through custom exception bubbling通过自定义异常冒泡报错
【发布时间】:2015-04-28 10:59:18
【问题描述】:

我有一个通过 WCF 进行通信的 C# 应用程序/服务链,以及一个 MVC Web 应用程序,它有时可以沿着这个服务链发起操作。

我想要的是一种安全的方式来报告此链中发生的有用错误,一旦网络应用用户发起导致异常的操作。我当前的解决方案是尽快捕获任何异常,然后使用最能识别导致问题的自定义消息创建一个 CustomException。此 CustomException 将通过 try/catch 块向上传播服务链(使用[FaultContract (typeof(SerialisedCustomException)] 在服务边界处序列化)。当在任何时候捕获到异常时,如果它是 CustomException 类型,它将被重新抛出。否则会像上面一样当场创建一个 CustomException,然后进一步抛出。

作为一个用例,假设登录到我的 Web 应用程序 A 的用户执行了一个操作,告诉服务 B 告诉服务 C 在 C 的文件系统上创建一个文件。 (实际动作比这更复杂,而且不止一件事情可能出错,但我认为这是一个足够的例子)

如果在此过程中出现任何问题,我希望以这样的方式通知用户,以便他可以解决问题。

通过 safe,我只想向用户显示我专门创建的错误(因此我使用了 CustomException),而不是其他任何内容(如果抛出系统异常,比如 UnauthorizedAccessException ,我将把它包装在我自己的 CustomException 中,并且只向最终用户显示我自己的 CustomException 的消息。我不希望向用户显示任何可能暴露实现细节的异常消息。

通过有用,我想向用户显示适当的消息和建议,例如“确保您拥有正确的权限等”,而不是 '服务 B 说:内部服务器错误'。(这些将在第一次抛出 CustomException 时设置,并且不会在进一步捕获/抛出时更改。如果启用调试,我还将显示堆栈跟踪,但在生产中我只会显示我的安全自定义消息)。

我的方法的问题是,我越来越多地围绕我的服务方法编写 try/catch 块,在它们调用的方法中等等,我觉得这不是正确的方法,并且有更好、更高效/不那么臃肿的解决方案。

有什么方法可以解决这个问题,同时避免将所有内容包装在 try/catch 块中的开销吗?

编辑:添加了需要报告错误的理由,而不是说“出了点问题,我们正在调查。”

我的网络应用可以在较低级别的服务上发起复杂的(和可定制的)操作。这些失败的原因有很多,可以通过调整该操作的自定义来解决。

例如,假设我有一个操作将用户输入的源文件复制到用户输入的目标位置(在机器 C 上,或在机器 C 可访问的网络驱动器上)。由于各种原因(未找到文件、网络关闭、缺少权限、磁盘空间不足等),此操作可能会失败,但某些原因可以通过用户更改该自定义操作来解决,前提是他们能够看看为什么它首先失败了。

【问题讨论】:

  • 那么所有这些尝试捕获 - 它们中的每一个都会产生一个单独的、对最终用户有意义的错误吗?
  • 希望是的。或者如果不是,则尽可能接近初始异常的来源。所以最坏的情况是你会得到一个“无法创建目录 X”而不是“复制操作失败”,例如。
  • 我无法摆脱这种感觉,即您正在尝试做的事情比实际做的要多。在这方面,Mehrzad Chehraz 的答案是正确的。如果您有用户可纠正的错误“找不到文件”,那么您需要一个一个地处理它们 - 所以这里没有解决这个问题,但是像“网络关闭”这样的其他错误只是一般性中断。另一个想法是在创建/排队操作之前添加验证检查,以最大程度地减少运行时出错的机会。文件存在吗?用户有权限吗?这种东西
  • 而且在您的情况下缺少权限也很可能是“系统错误”而不是用户错误,尤其是当我们谈论文件系统权限时。这不像用户可以更改服务的文件系统权限以访问文件。我猜整个事情需要在更广泛的背景下考虑。您的应用程序到底在做什么以及如何做的?可能是您需要在其他地方更改设计以避免这里出现问题。

标签: c# .net wcf exception


【解决方案1】:

您不需要尝试...捕获所有内容,您只需要捕获预期的异常以将它们包装在 CustomException 中并向它们添加有用的消息,否则它们应该在调用者(或调用者的调用者)中被捕获等等)方法。 此外,您不需要捕获 CustomException 并重新抛出它。即使你没有抓住它,它也会冒泡。唯一的问题是 WFC 服务仅抛出 FaultExceptions。您只能在最外部的调用中捕获这些异常。

要获得有用的异常,您可以简单地提供异常的详细消息,为 CustomException 指定 ErrorCode 属性或定义嵌套异常,如继承 CustomException 的 CustomException1 和 CustomException2。在 UI 级别,您可以从该错误代码或异常类型中解决用户友好的错误消息。

例如:

Web 应用程序 A:

void UserAction(string input) {
    try {
        serviceB.RespondToAction(input);
    }
    catch (FaultException e) {
       if (e.InnerException is CustomException) {
          DisplayMessageToUser(ResolveExceptionMessage((CustomException)e.InnerException));
       }
    }
    catch (CustomException e) {
          DisplayMessageToUser(ResolveExceptionMessage(e));         
    }
}

服务 B:

void RespondToAction(string input) {
    try {

        SomeOperation(input);
    }
    catch (FormatException e) {
        throw new CustomException("Invalid input format.", e);
    }
    // Note you do not catch CustomException here...

    // Note you do not use try-catch here...
    serviceC.CreateFile();
}

服务 C:

void IOOperation2() {
     try {
         IOOperation();
     }
     catch (IOException e) {
         throw new CustomException("Failed to create file.", e);
     }
     catch (SecurityException) {
         throw new CustomException("Failed to create file.", e);
     }
     // Note you do not catch an ExecutionEngineException here...
}

【讨论】:

    【解决方案2】:

    根据我的经验,异常对于向最终用户传达信息很少有用。 “确保您拥有正确的权限等。” 听起来更像是一种业务规则类型的处理,而不是异常类型。

    我也不经常遇到通过层传播异常的需要。在某些情况下,这可能很有用,但大多数情况下,它们可以在它们被抓到的地方被记录下来。

    并且 catch 子句通常非常谨慎地使用,也就是说,Web 应用程序只有一个全局异常处理程序并不少见,仅此而已。

    异常的主要目的是告诉支持人员出了什么问题。这主要是a)基础设施条件(服务器停机,数据库停机等)或b)代码中的错误。其他所有内容通常都属于业务错误并按此处理。

    上述情况还不够,例如,如果您正在使用您无法控制的服务或库,并且此服务器/库利用异常来传达操作的结果。但这种情况很少见。

    我设计异常处理程序的方式是始终从一个全局处理程序开始(每个应用程序)。然后,如果在测试期间发现异常,我会查看它们并尝试对它们进行分类。这个异常是因为错误吗?然后我修复错误。这个例外是因为基础设施条件吗?然后可能需要记录它,向客户显示适当的消息,并可能通知支持。在这种情况下,可以保证自定义错误处理程序,但通常是通用的“我们有一个错误,我们正在调查它”消息,并且支持通知就足够了,在这种情况下,也不需要自定义错误处理程序。请注意,在所描述的情况下,最终用户实际上无法对问题做任何事情,只能等待。最后,如果异常是由于业务条件造成的,那么我看看是否可以重新设计代码路径,以便不会因业务类型错误而引发异常。如果是我自己的代码,这不是问题(我不会有这种情况开始),如果它是库/服务,那么是的,可能需要将调用包装到 try/catch 中。

    由于上述方法的结果,我的代码几乎没有异常处理程序,我喜欢这种方式 - 如果发生灾难,通常会立即清楚堆栈跟踪精确定位的确切位置,并且没有混乱的嵌套异常考虑一下。

    注意:对于图书馆作者来说,考虑因素可能略有不同

    【讨论】:

    • 我在最后添加了一个理由,因为我需要查看错误消息。
    • @user241543903,是的,这完全有道理。
    猜你喜欢
    • 1970-01-01
    • 2015-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-13
    • 1970-01-01
    • 2011-02-23
    相关资源
    最近更新 更多