System.Threading.ThreadAbortException caused by Response.Redirect

Tapas Bose picture Tapas Bose · Apr 1, 2012 · Viewed 16.8k times · Source

In my application I am calling a WebMethod from JavaScript, where I am trying to redirect to some page:

[WebMethod]
public string Logout() {            
    if (User.Identity.IsAuthenticated) {                            
        HttpContext.Current.Response.Redirect("~/Pages/Logout.aspx");               
    }
    return "";
}

The aspx page:

    <input onclick="callLogout();" id="btn" type="button" value="Click Me" />

    <asp:ScriptManager ID="ScriptManager" runat="server">
        <Services>
            <asp:ServiceReference Path="~/WebServices/EMSWebService.asmx" />
        </Services>
    </asp:ScriptManager>
    <script type="text/javascript">        
        function callLogout() {
            EMSApplication.Web.WebServices.EMSWebService.Logout(OnComplete, OnError);
        }

        function OnComplete(result) {
            alert(result);
        }

        function OnError(result) {
            alert(result.get_message());
        }
    </script>

And I am getting:

A first chance exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll

An exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll but was not handled in user code

in my VS2010's Output window.

Why I am getting this exception and how can I resolve this?

Answer

Sam picture Sam · Jun 26, 2012

The ideal way to redirect is to call Response.Redirect(someUrl, false) and then call CompleteRequest()

Passing false to Response.Redirect(...) will prevent the ThreadAbortException from being raised, however it is still important to end the page lifecycle by calling CompleteRequest().

When you use this method in a page handler to terminate a request for one page and start a new request for another page, set endResponse to false and then call the CompleteRequest() method. If you specify true for the endResponse parameter, this method calls the End method for the original request, which throws a ThreadAbortException exception when it completes. This exception has a detrimental effect on Web application performance, which is why passing false for the endResponse parameter is recommended. For more information, see the End method.

Note that when the Response.Redirect(...) method is called, a new thread is spawned with a brand new page lifecycle to handle the new redirected response. When the new response finishes, it calls Response.End() on the original response which eventually throws a ThreadAbortException and raises the EndRequest event. If you prevent Response.End() from being called (by passing false to Response.Redirect) then you need to call CompleteRequest() which:

Causes ASP.NET to bypass all events and filtering in the HTTP pipeline chain of execution and directly execute the EndRequest event.

Word of Caution:

If you call Response.Redirect(someUrl, false) allowing code continue to execute, you may want to change your code such that the processing gracefully stops. Sometimes this is as easy as adding a return to a void method call. However, if you are in a deep call stack, this is much trickier and if you don't want more code executing it might be easier to pass true like Response.Redirect(someUrl, true) and purposely expect the ThreadAbortException - which by the way isn't a bad thing, you should expect it during Response.Redirect(...) and Server.Transfer(...) calls.

ThreadAbortException Cannot Be Stopped By Catching the Exception

The ThreadAbortException is not your ordinary exception. Even if you wrap your code in a try catch block, the ThreadAbortException will immediately be raised after the finally clause.

When a call is made to the Abort method to destroy a thread, the common language runtime throws a ThreadAbortException. ThreadAbortException is a special exception that can be caught, but it will automatically be raised again at the end of the catch block. When this exception is raised, the runtime executes all the finally blocks before ending the thread. Because the thread can do an unbounded computation in the finally blocks or call Thread.ResetAbort to cancel the abort, there is no guarantee that the thread will ever end. If you want to wait until the aborted thread has ended, you can call the Thread.Join method. Join is a blocking call that does not return until the thread actually stops executing.

Often what I've seen in code is a try catch block around Response.Redirect that will log exceptions that are not ThreadAbortExceptions (since you expect those). Example:

private void SomeMethod()
{
   try
   {
      // Do some logic stuff
      ...

      if (someCondition)
      {
         Response.Redirect("ThatOneUrl.aspx", true);
      }
   }
   catch (ThreadAbortException)
   {
      // Do nothing.  
      // No need to log exception when we expect ThreadAbortException
   }
   catch (Exception ex)
   {
      // Looks like something blew up, so we want to record it.
      someLogger.Log(ex);
   }
}