Maybe you have noticed that using the Response.Redirect("RedirectDestination.aspx"); method causes ThreadAbortExceptions to be thrown. A simple explanation and a workaround is given on the Microsoft's support website. Unfortunately, the explanation found there is not fully satisfactory, at least not for me.
First, if ThreadAbortExceptions are thrown, how come they are not caught in the page OnError overload? Here is the code:
protected override void OnError(EventArgs e)
{
Exception ex = Server.GetLastError();
Server.ClearError();
Response.Write("Error:" + ex.Message);
Response.End();
}
protected void Button1_Click(object sender, EventArgs e)
{
Response.Redirect("RedirectDestination.aspx");
}
With this code above, the application never enters the OnError method, so the exception is not caught this way. But if we put the Redirect in a try .. catch block, then the exception is caught; even if the redirect is actually performed, the code from the catch block is executed:
protected
void Button1_Click(object sender, EventArgs e)
{
try
{
Response.Redirect("RedirectDestination.aspx");
}
catch (Exception ex)
{
Response.Write("Error:" + ex.Message);
Response.End();
}
}
Let's do some digging and find out more about this behaviour. For this, we use one of my favourite tools, the .NET Reflector, to be able to read the code from the System.Web.HttpResponse and System.Web.UI.Page classes. If we look inside the code implementation for Redirect(string url), we see that Redirect(string url, bool end) is called, with the boolean parameter set to true. This means, that the Response.End() method is called. Inside this method, we see a call to the Thread.CurrentThread.Abort method, which obviously aborts the current thread, so the response of the current page can be ended. The termination of the current thread is actually done by throwing a ThreadAbortException. This explains why calling Response("some url", false) does not throw this kind of exceptions.
Ok, now let's try to find out why the exception is not caught in the OnError overload. In order to see whos is responsilbe for executing the code for an ASP.NET page (all the events, including Init, Load, Render), we can check the stack trace of the method calls before the Page_Load; for this we can throw an Exception in the OnLoad event and write it's stack trace to the response, or in the Trace, in the OnError method. Then, we see that a method called ProcessRequestMain is called some time before OnLoad. Getting to the code of this method, again using the .NET Reflector, gives us the answer to our question: ThreadAbortException is caught and not rethrown - that's why we cannot catch it in OnError.
I am still wondering why the Redirect overload with a single string parameter is public, but at least I know how to use this method now: either call the overload with a second boolean parameter set to false (as Microsoft suggests), or catch the ThreadAbortException (but not in OnError as we have seen). You can find the source code for this post here.