Matlus
Internet Technology & Software Engineering

ASP.NET Response.Redirect Performance Issue

Posted by Shiv Kumar on Senior Software Engineer, Software Architect
VA USA
Categorized Under:  
Tagged With:  

This is a short post and I'm writing this because I've seen a lot of people using Response.Redirect(url) or Response.RedirectPremanent(url) and not using the overloads:

  • Response.Redirect(url, false)
  • Response.RedirectPermanent(url, false)

So rather than repeat the same thing over and over, I've decided to write a blog post about it in the hopes that others may find it and learn from it. There are two overloads to these methods (I'm only showing the Response.Redirect method but the same applies to Response.RedirectPermanent):

Response.Redirect(string url)
Response.Redirect(string url, bool endResponse)

You typically use Response.Redirect when we want to redirect a request to another page in our application or another website or a send an Http status 301 Permanently moved or something similar. In our code the way that this works is that the line right after the call to redirect does not execute. Indeed, most times (if not all the time) this is exactly the behavior we want/expect. It's like (in C#) saying return and the execution jumps right out of the current method. It makes structuring your flow of logic that much easier.

Here's the problem. Think about how, when you call Response.Redirect(url) the next line in our code is not executed (without explicitly saying return)? Yup, that's right, an exception was thrown. That's the only way to boot right out of a method without explicitly saying return. When we call Response.Redirect(url), it calls the second overload passing in true as the parameter to endResponse. What this does is, it calls Response.End(), and Response.End() throws a ThreadAbortException (that's the only way it can truly end the response).

So what we should do is use the second overload (always) and pass it false as the second parameter (always). But….but…but, yes, I know, in that case our code continues to execute and that's not what we want!

There are two solutions to this predicament.

  1. Structure our code such that the condition that warrants a Response.Redirect follows a path such that nothing else is done in our Page/Handler/Controller and our method ends gracefully.
  2. Call CompleteRequest() right after calling Response.Redirect(url, false). This is a method of the ApplicationInstance object, which we can get from the current context, so something like HttpContext.ApplicationInstance.CompleteRequest();. Note that the execution of code will continue as normal (and not exit/abort). But what will happen is that as soon as the Page/Handler/Controller completes the response the ASP.NET pipeline will jump straight to the EndRequest event. So the Request does "complete" (without any ThreadAbortException), but your code also executes. This may not be something you want to happen. But I thought I'd mention it anyway.

As regards option 1 above, here is a code listing that show you what I mean. Think of the BuildPage method shown below as the ProcessRequest method of a handler. That is once this method finishes we're done processing the request (this is code I've pulled out from Orion by blog engine). There is only one case in which we want to process the request (the if condition). All other cases warrant either a permanent redirect or redirect. All of the code expected to be executed under the normal case is inside the if condition.

 

    protected override void BuildPage(HttpContextBase httpContext, object model)
    {
      var postDisplayDataDto = (PostDisplayDataDto)model;
      if (model != null && postDisplayDataDto.PostsDataTable.Rows.Count == 1)
      {
        //Ttile of the Post + On + Blog name
        TitleView.Text = postDisplayDataDto.PostsDataTable.Rows[0][4].ToString() + " On " + BlogInfo.BlogInfoName;
        //Title of the Post plus the Post Text
        MetaDescriptionView.Description = postDisplayDataDto.PostsDataTable.Rows[0][4].ToString() + " " + postDisplayDataDto.PostsDataTable.Rows[0][5].ToString();
 
        RegisterView("Body", new PostView(httpContext, postDisplayDataDto,
          Configuration["CategoryCaption"],
          Configuration["TagCaption"]
          ));
      }
      else if (model == null)
        HttpContext.Response.RedirectPermanent(Url.Post(AppDomainAppVirtualPath, PostSlug), false);
      else
        PermanentlyRedirectToAppropriateLocation();
    }

Showing one way to gracefully exit from a method