通过自定义异常冒泡报告错误

时间:2015-04-28 10:59:18

标签: c# .net wcf exception

我有一系列通过WCF进行通信的C#应用​​程序/服务,以及一个MVC Web应用程序,它有时可以在这个服务链上发起行动。

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

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

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

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

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

我的方法的问题是我越来越多地围绕我的服务方法编写try / catch块,并且在他们调用的方法中,等等,最让我觉得这不是正确的方法并且那里有一个更好,更有效/更少膨胀的解决方案。

有没有解决这个问题的方法,同时还避免了在try / catch块中包装所有内容的开销?

编辑:为需要报告错误添加了理由,而不是说'出了问题,我们正在研究它。'

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

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

2 个答案:

答案 0 :(得分:0)

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

我不经常遇到通过图层传播异常的需要。有些场景可能会有用,但大多数情况下它们都可以在被捕获的站点上进行记录。

并且通常非常谨慎地使用被捕获的子句,也就是说,Web应用程序只有一个全局异常处理程序并且不常见。

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

当上述情况不充分时会出现边缘情况,例如,如果您正在使用您无法控制的服务或库,并且此服务器/库使用异常来传达操作结果。但这很罕见。

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

作为上述方法的结果,我的代码只有非常少的异常处理程序,我喜欢这种方式 - 如果发生灾难,通常会立即清楚堆栈跟踪精确定位的位置,并且没有要考虑凌乱的嵌套异常。

注意:对于图书馆作者,注意事项可能略有不同

答案 1 :(得分:0)

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

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

例如:

网络应用程序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...
}