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