理解ASP .NET (WebForms)中的错误处理

3
我有一个Web表单(.NET 4.0)Web应用程序,构建为:
  • MyApp.Portal(主门户,包括页面、WCF AJAX服务、脚本等)
  • MyApp.BusinessLayer(类库调用DataAccess获取数据)
  • MyApp.Services(类库包含WCF服务器用于服务器端代码)
  • MyApp.DataAccess(ADO Oracle)
  • 我正在尝试遵循一些错误处理教程,但目前我得到的结果不一致。
    我的问题是,我应该如何处理我的应用程序中的错误? 我应该在所有方法中都使用try/catch包装吗? 我只需要在业务层中的方法中使用try/catch包装吗? 如果出现WCF Ajax方法的PortalLayer中的错误,我该如何处理?
    例如,我将以下内容添加到Global.asax文件中:
    void Application_Error(object sender, EventArgs e)
            {
                // Code that runs when an unhandled error occurs
                Exception exc = Server.GetLastError();
    
                if (exc is HttpUnhandledException)
                {
                    if (exc.InnerException != null)
                    {
                        exc = new Exception(exc.InnerException.Message);
                        Server.Transfer(@"\Pages\Error.aspx?handler=Application_Error%20-%20Global.asax", true);
                    }
                }
    
            }
    

    然而,大部分时间这个方法并没有被调用。我只能看到一个错误信息显示在屏幕上(就像一个高级的警告)。
    或者如果这个方法被调用了,那么异常不是HttpUnhandledException,所以传输就永远不会发生。

    我也尝试过在我的web.config中添加这个代码,但我觉得它好像没什么作用。(如果我注释掉它,我得到的结果是一样的)

    <customErrors mode="On" defaultRedirect="Error.aspx?handler=customErrors%20section%20-%20Web.config">
      <error statusCode="404" redirect="ErrorPage.aspx?msg=404&amp;handler=customErrors%20section%20-%20Web.config"/>
    </customErrors>
    

    最终目标是在重定向用户到页面(或仅将错误显示得漂亮)的同时,将错误日志记录到文件中或将其写入数据库。


    Application_Error不会被404调用,因为这发生在应用程序运行之前。实际上,如果你想一想,这是有道理的。如果它甚至找不到要运行的应用程序,它怎么能运行处理程序呢?此外,Web服务器本身处理许多种Http错误,并且您必须告诉服务器如何处理它们。 - Erik Funkenbusch
    你的问题非常广泛,没有一个完美或正确的答案。是的,你可以在任何地方“不处理”异常,只在UI层中处理,但从长远来看这是有害的。通常有两种类型的异常:执行异常和业务异常。执行异常是指运行时发生的情况。业务异常是指当你做与你的业务相关的事情,并且你遇到了条件,比如说,你不能再有更多的订单,你就会抛出异常,然后你处理每个异常。对于业务异常,你需要描述为什么不能继续。 - T.S.
    对于运行时异常,你只需说“意外错误”,但在事件日志中记录所有信息。例如,我喜欢UI以请求-响应方式与BLL通信。UI发送请求-获取订单25的订单详细信息。BLL发送响应-这里是数据和成功标志以及错误消息(如果success != true)。这样,所有UI需要做的就是显示消息或填充数据。它可以拦截其本地UI异常。如果您有这种通信方式,您可以随时更改UI,拥有多个UI客户端等。 - T.S.
    1个回答

    5

    void Application_Error(object sender, EventArgs e)会在服务器处理页面(请求到.aspx、.ashx资源)时发生主线程异常时被调用。

    以下两种情况不会被调用:

    • 在处理.aspx资源时启动新线程且异常发生在新线程中
    • 在Web服务方法、WCF方法等非.aspx资源中
    • 异常是“坏”的异常——会破坏应用程序域——包括StackOverflowExceptionOutOfMemoryException

    因此,对于WCF、Web服务等,最好将所有入口点都包装起来。

    try {} 
    catch(Exception e) {
      Logger.Log(e);
      throw;
    } 
    

    注意,如果有很多这样的语句,请花时间寻找更通用的解决方案。

    还要注意 - 如果您的错误页面上有错误,您将获得黄色死屏(显然)。因此,我更喜欢显示静态HTML页面作为错误页面(最低的错误概率)。

    在您的错误处理代码中,您没有清除错误状态,而且我更喜欢使用Redirect来处理错误,而不是使用Server.Transfer。最终代码段如下:

     var ex = Server.GetLastError();
     Logger.Log(ex);
     Server.ClearError();
     Response.Redirect("/error.aspx");
    

    最后提醒-不需要自己编写错误日志(好的程序员是懒的人-不会重新造轮子)-有很多很好的日志记录模块,如Elmah、Microsoft Enterprise Library或log4net。


    我已经实现了log4net,它很好用。但是在WCF服务的情况下,如果出现错误并抛出消息,我希望将其显示在自定义错误页面上。目前它只是抛出到屏幕上。(我看不到YSOD)我需要在每个catch语句中进行重定向吗? - John Doe
    对于未处理的WCF异常,一个好的处理方式是实现一个WCF全局异常处理程序 - 即实现自己的IErrorHandler并将其注册为行为在web.config中。同样,对于Web API,请注册自己的System.Web.Http.ExceptionHandling.ExceptionHandler。这样,您就不需要在每个方法中包装try/catch。 - Simon Brangwin

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接