使用.NET 3.5,ASP.NET,Enterprise Library 4.1异常处理和日志记录块,我编写了一个自定义异常处理程序来显示标准错误页面,如下所示:
[ConfigurationElementType(typeof(CustomHandlerData))]
public class PageExceptionHandler : IExceptionHandler {
public PageExceptionHandler(NameValueCollection ignore) {
}
public Exception HandleException(Exception ex, Guid handlingInstanceID) {
HttpResponse response = HttpContext.Current.Response;
response.Clear();
response.ContentEncoding = Encoding.UTF8;
response.ContentType = "text/html";
response.Write(BuildErrorPage(ex, handlingInstanceID));
response.Flush();
//response.End(); // SOMETIMES DOES NOT WORK
return ex;
}
}
这是从这样的异常处理策略中调用的:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="exceptionHandling" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration.ExceptionHandlingSettings, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</configSections>
<exceptionHandling>
<exceptionPolicies>
<add name="Top Level">
<exceptionTypes>
<add type="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
postHandlingAction="None" name="Exception">
<exceptionHandlers>
<add logCategory="General" eventId="0" severity="Error" title="Application Error"
formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
priority="0" useDefaultLogger="false" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
name="Log Full Details" />
<add type="PageExceptionHandler, Test, Culture=neutral, PublicKeyToken=null"
name="Display Error Page" />
</exceptionHandlers>
</add>
</exceptionTypes>
</add>
</exceptionPolicies>
</exceptionHandling>
</configuration>
一切正常,除了错误页面被合并到错误发生时显示的任何标记中。我认为将response.End()
添加到异常处理程序中可以解决此问题。它确实如此,但后来我发现,有时,ASP.NET会在尝试结束响应流时抛出ThreadAbortException
。这是唯一无法捕获和忽略的异常类型。哎呀!谁能告诉我:
ThreadAbortException
?response.End()
更安全的替代方案吗?response.End()
之前检测响应流是否“可以”? li>
醇>
编辑 - 更多背景
此代码的设计目标是尽可能简单地将EHAB样式的错误处理添加到Web应用程序。我有一个必须添加到IHttpModule
文件的自定义web.config
。在模块的Application_Error
事件中,它调用ExceptionPolicy.HandleException
。必须将异常处理策略配置为调用上述PageExceptionHandler
类。整个过程只涉及少量XML配置,没有额外文件,并且在使用Web应用程序中没有额外的代码行。
事实上,现在的代码似乎工作正常。但是,在某些情况下,需要在Web应用程序代码中具有直接调用异常处理策略的显式catch块。这种情况是不能正常工作的。我想要一个可以在所有可能的调用方法下工作的解决方案。我的印象是,如果没有参与EHAB会更简单,但不幸的是它在我们所有的代码中使用,并提供了许多其他好处,我宁愿保留它。
编辑 - 测试结果
我创建了一个测试工具,以六种不同的方式练习代码:
HttpResponse
。ExceptionPolicy.HandleException(ex, "Top Level")
。ExceptionPolicy.HandleException(ex, "Top Level")
。Page_Error
事件调用ExceptionPolicy.HandleException(ex, "Top Level")
。Application_Error
事件调用ExceptionPolicy.HandleException(ex, "Top Level")
。IHttpModule
课程调用ExceptionPolicy.HandleException(ex, "Top Level")
。在编写错误页面标记后,没有代码终止响应的每个方法的测试结果:
调用HttpContext.Current.ApplicationInstance.CompleteRequest
时每种方法的测试结果:
调用HttpContext.Current.Response.End
时每种方法的测试结果:
ExceptionHandlingException
两次。调用HttpContext.Current.Response.End
时的每个方法的测试结果,但包含在try
... catch
块中:
ThreadAbortException
,它被catch块捕获并吸收。ThreadAbortException
以及两个ExceptionHandlingException
s。这是一组荒谬的行为。 : - (
答案 0 :(得分:2)
查看ELMAH ELMAH上有很多文章和操作方法,只是google或bing for it :-)
<强>优点强>
+它几乎记录了所有内容
+电子邮件通知
+远程查看错误
我建议使用它并重定向到默认错误页面,正如Ariel所说。
答案 1 :(得分:0)
看看这个Microsoft explanation。它说它适用于ASP.Net 1.1,但我认为它仍然适用于2.0。基本上你应该调用HttpContext.Current.ApplicationInstance.CompleteRequest
方法。
另外,我不确定为什么你不能抓住ThreadAbortException
- 为什么你不能做像
try {
// code
} catch (ThreadAbortException)
{
//do nothing
}
答案 2 :(得分:0)
我处理ASP.net异常的方法总是抓住它,记录它然后重定向到带有一些标准错误消息的通用错误页面(最终用户不关心异常或堆栈跟踪)。 您已经在处理程序之前在策略中记录了异常,因此您可能需要做的只是将响应重定向到错误页面并在那里移动BuildErrorPage逻辑。您可以在查询字符串中传递handlingInstanceId。
答案 3 :(得分:0)
尝试在Global.asax中使用Application_Error来捕获应用程序中任何页面抛出的任何非处理异常。
答案 4 :(得分:0)
这可能有所帮助:http://msdn.microsoft.com/en-us/library/cyayh29d.aspx
具体地 “你的清理代码必须在catch子句或finally子句中,因为系统在finally子句的末尾重新抛出ThreadAbortException,或者如果没有finally子句则在catch子句的末尾重新抛出强>
您可以通过调用Thread .. ::。ResetAbort方法来阻止系统重新抛出异常。但是,只有在您自己的代码导致ThreadAbortException时才应该这样做。 “
请注意,Response.End和Response.Redirect可能会在某些条件下抛出ThreadAbortException。
答案 5 :(得分:0)
经过大量测试后,我发现只有两种方法可以正常运行。
解决方案1
根本没有改变当前的架构。全局异常处理程序已按预期工作。如果由于任何原因在代码中需要显式的catch块(例如,在输入表单上呈现验证错误),那么我将告诉开发人员明确地调用Response.End
,例如。
try {
// ...
} catch(Exception ex) {
if(ExceptionPolicy.HandleException(ex, "Top Level")) {
throw;
}
Response.End();
}
解决方案2
放弃尝试覆盖当前的响应流并执行真正的重定向到错误页面。这适用于所有六种测试工具方法。下一个问题是找到一种方法,这需要对消费项目进行尽可能少的更改,并抽象掉所有脏的细节。
步骤1 - 向项目添加占位符ErrorPage.ashx
文件,该文件仅包含对标准网页处理程序类的引用,不包含代码隐藏。
<%@ WebHandler Class="WebApplicationErrorPage" %>
第2步 - 使用会话密钥在PageExceptionHandler
课程中重定向到此页面,以传递所有必需的数据。
public Exception HandleException(Exception ex, Guid handlingInstanceID) {
HttpResponse response = HttpContext.Current.Response;
HttpSessionState session = HttpContext.Current.Session;
session["ErrorPageText"] = BuildErrorPage(ex, handlingInstanceID);
response.Redirect(errorPageUrl, false);
return ex;
}
步骤3 - 使WebApplicationErrorPage
类成为一个非常愚蠢的HTTP处理程序,只写入响应流。
public class WebApplicationErrorPage : IHttpHandler, IReadOnlySessionState {
public bool IsReusable {
get {
return true;
}
}
public void ProcessRequest(HttpContext context) {
response.Clear();
response.ContentEncoding = Encoding.UTF8;
response.ContentType = "text/html";
response.Write((string) context.Session["ErrorPageText"]);
response.Flush();
}
}
<强>结论强>
我认为我会坚持使用解决方案1,因为它不涉及切碎和更改现有架构(实现解决方案2将比上面提到的代码片段复杂得多)。我将提出解决方案2,以便在其他地方使用。