Exception Handling Exception handling in an application block in ASP.NET cannot call Response.End ()

Using .NET 3.5, ASP.NET, Corporate Library 4.1. Blocking exceptions and logging, I wrote a special exception handler to display the standard error page as follows:

[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; } } 

This is called from an exception handling policy, like this:

 <?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> 

Everything works fine, except that the error page was included in any markup displayed at the time the error occurred. I thought adding response.End() to the exception handler would solve this problem. This happened, but I noticed that sometimes ASP.NET throws a ThreadAbortException when trying to terminate the response flow. This is the only type of exception that cannot be caught and ignored. Hmm! Can anyone tell me:

  • Under what conditions will ASP.NET throw a ThreadAbortException when trying to terminate a response flow?
  • Is there a safer alternative to response.End() that I could use?
  • If not, is there a way to determine if the response stream is β€œcomplete” before calling response.End() ?

EDIT - More Context

The goal of the project for this code is to make it as simple as possible to add EHAB-style error handling to a web application. I have a custom IHttpModule that needs to be added to the web.config . In the Application_Error module event, it throws an ExceptionPolicy.HandleException . An exception handling policy must be configured to call the PageExceptionHandler class described above. This entire procedure includes only a small number of XML configurations, additional files, and additional lines of code in a consuming web application.

In fact, code like it seems to be working fine. However, in some situations, it is desirable to have an explicit catch block in the web application code that invokes the exception handling policy directly. Such a scenario is something that does not work properly. I would like one solution to work under all possible ways of calling it. I get the impression that it would be much easier if EHAB were not involved, but, unfortunately, it is used throughout our code and provides so many other benefits that I would prefer to keep.

EDIT - test results

I created a test harness to implement the code in different ways:

  • Direct write to the current HttpResponse page.
  • Building an exception object and calling ExceptionPolicy.HandleException(ex, "Top Level") directly.
  • Throwing an exception object, catching it, and calling ExceptionPolicy.HandleException(ex, "Top Level") in the catch block.
  • Throwing an exception object and Page_Error event ExceptionPolicy.HandleException(ex, "Top Level") .
  • Throwing an exception object and throwing Application_Error on the ExceptionPolicy.HandleException(ex, "Top Level") event ExceptionPolicy.HandleException(ex, "Top Level") .
  • Throwing an exception object and calling my custom call IHttpModule ExceptionPolicy.HandleException(ex, "Top Level") .

Test results for each method without code to complete the response after recording the markup of the error page:

  • Methods 1, 2, 3 - The layout of the error page in combination with the layout of the test page.
  • Methods 4, 5, 6 - The layout of the error page replaced the layout of the test page (desired result).

Test results for each method when HttpContext.Current.ApplicationInstance.CompleteRequest was called:

  • Methods 1, 2, 3 - The layout of the error page in combination with the layout of the test page.
  • Methods 4, 5, 6 - The layout of the error page replaced the layout of the test page (desired result).

Test results for each method when HttpContext.Current.Response.End was called:

  • Methods 1, 5, 6 - The layout of the error page has replaced the layout of the test page (desired result).
  • Methods 2, 3, 4 - Error page markup replaced test page markup, but EHAB throws an ExceptionHandlingException twice.

Test results for each method when HttpContext.Current.Response.End was called, but wrapped in a try ... catch :

  • Methods 5, 6 - The layout of the error page replaced the layout of the test page (desired result).
  • Methods 1, 3, 4 - The error page markup replaced the test page markup, but ASP.NET throws a ThreadAbortException that is caught and consumed by the catch block.
  • Method 2 - The error page markup replaced the test page markup, but I get a ThreadAbortException , as well as two ExceptionHandlingException s.

This is a funny set of behaviors. :-(

+4
source share
6 answers

After much testing, I found only two approaches that work without errors.

Solution 1

No changes to the current architecture at all. The global exception handler is already running as desired. If an explicit catch block is necessary for the code for some reason (for example, to visualize validation errors on the input form), I will tell the developers to explicitly call Response.End , for example.

 try { // ... } catch(Exception ex) { if(ExceptionPolicy.HandleException(ex, "Top Level")) { throw; } Response.End(); } 

Decision 2

Refuse attempts to rewrite the current response flow and make a genuine redirect to the error page. This works great with all six validation methods. The next problem was to find a way to do this, requiring the smallest possible changes in the consumption design and distract all the dirty parts.

Step 1: Add the placeholder ErrorPage.ashx to the project, which contains only a link to the standard class of the web page handler and the lack of code.

 <%@ WebHandler Class="WebApplicationErrorPage" %> 

Step 2 - Reassign this page in the PageExceptionHandler class, using the session key to pass all the necessary data.

 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; } 

Step 3. Make the WebApplicationErrorPage class a really dumb HTTP handler that simply writes to the response stream.

 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(); } } 

Conclusion

I think I will stick to solution 1 because it is not related to shredding and changing the existing architecture (implementing solution 2 for the real one will be much more complicated than the code snippets suggested above). I will write solution 2 for possible use elsewhere.

0
source

Take a look ELMAH .
There are many articles and practical recommendations on ELMAH, just for Google or for it :-)

Benefits
+ He logs almost everything
+ Email Notification
+ Remote error viewing

I would suggest using this and redirecting to the default error page, as Ariel mentioned.

+2
source

Take a look at Microsoft's explanation . He says this applies to ASP.Net 1.1, but I think it still applies in version 2.0. Basically you should call the HttpContext.Current.ApplicationInstance.CompleteRequest method.

Also, I'm not sure why you cannot catch a ThreadAbortException - why you cannot do something like

 try { // code } catch (ThreadAbortException) { //do nothing } 
0
source

My approach to handling ASP.net exceptions has always been to catch it, log it, and then redirect to a common error page with some standard error message (the end user will not care about exceptions or stack tracing). You have already registered an exception in your policy before your handler, so perhaps all you need to do is redirect the response to the error page and move the BuildErrorPage logic there. You can pass the InstanceId handler in the request.

0
source

Try using Application_Error in Global.asax to catch any unhandled exceptions thrown by any page in your application.

0
source

This may help: http://msdn.microsoft.com/en-us/library/cyayh29d.aspx

Specifically, "Your cleanup code must be in the catch clause or in the finally clause because the ThreadAbortException is thrown by the system at the end of the finally clause or at the end of the catch clause if there is no final clause .

You can prevent the exception from being re-created by calling the Thread .. :: method. ResetAbort. However, you should only do this if your own code throws a ThreadAbortException. "

Note that Response.End and Response.Redirect may throw a ThreadAbortException under certain conditions.

0
source

Source: https://habr.com/ru/post/1286804/


All Articles