.NET Ajax 1.0, Server.Transfer & Not Rewriting Our Entire Application

The application I am currently working on has a hand rolled implementation of Ajax that is somewhat awkward to work with. At the end of our last sprint, I was tasked with spiking what it would take to migrate to the .NET Ajax 1.0 extensions while everyone else was working on release week tasks.

It took a couple days to figure out all the installs, web.config modifications, component vendor compatibility and other various things. But in the end, I had a simple page with two sample Ajaxified controls that worked.

I then turned to the task of unraveling the way the current implementation was wired up and modifying a single page to use the Ajax framework instead. A few minor safe refactorings and quirks later, I had a semi-functional reference implementation with one giant problem.

The page contained a series of drop down lists that filter each other as you make selections. On initial page load, I could select an item any drop down and it would appropriately modify the others. The second selection always returned the following error:

Sys.WebForms.PageRequestManagerServerErrorException: An unknown error occurred while processing the request on the server. The status code returned was: 404
Reguardless of what list I selected from first, the second request would fail. My IIS logs revealed that my request was being sent to server, but for whatever reason the URL changed.
2008-10-17 14:52:14 W3SVC1 127.0.0.1 POST /Aware/Xtend/mParticipant/NewPlannedService.aspx  80 - 127.0.0.1 200 0 0

2008-10-17 14:52:20 W3SVC1 127.0.0.1 POST /Aware/mParticipant/NewPlannedService.aspx  80 - 127.0.0.1 404 0 0
Using Fiddler, I was able to see that a value for formAction was being sent back to the client that was invalid. You can also see that the second request is going to a non-existent page:

I was able to resolve the issue by explicitly setting the value of my forms action back to the correct path at the end of my call back method like so:

{% highlight c# %}
this.Form1.Action = Request.Url.PathAndQuery
{% endhighlight %}

But this seemed like a hack in the worst way and smelled somewhat like moldy cheese to me. And I still didn't understand what was causing the issue.

The application makes extensive use of Server.Transfer as a "performance enhancement". I noticed that the form I was working with was one of these cases where Page1.aspx transfers execution to my Ajaxified page.

I decided to try to isolate the behavior and reproduce it in a simple example project.

I fired up Visual Studio and created a new Ajax website. I added two pages; Default.aspx and AjaxPage.aspx. I implemented a simple ajax call back on my ajax page and the default page transfer execution in its Page_Load method.

To my surprise, it worked with no problems. I didn't see the nasty error at all. I verified with Fiddler. I was stumped.

I started thinking about the behavior in my application. The only difference I could think of was that the page that was being transferred to was in a different subdirectory than the original page. So I created a folder in my test project and moved AjaxPage.aspx into it. And this time I got the error. I confirmed with Fiddler that my "Folder" sub directory was being dropped off the second request and that it was missing from the response from the first request.

My smelly cheese hack worked as well for my test project. But I didn't want to solve this issue with a server side hack. So I created the following client side Javascript to hook into the client ajax framework and solve it there in a single place:

var orginalFormAction = null;

//capture the current form action value
function BeginRequestHandler() {
  orginalFormAction = theForm.action;
}

//set the form action value back to the
//correct value
function EndRequestHandler() {
  theForm.action = orginalFormAction;
  theForm._initialAction = orginalFormAction;
}

function RegisterRequestHandlers() {

  if (typeof (Sys) != "undefined") {

    Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler);
    Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(BeginRequestHandler);
    Sys.Application.notifyScriptLoaded();

  }
}

//register request handlers after the application 
//has successfully loaded.
Sys.Application.add_load(RegisterRequestHandlers);

I can then add the script to any ajax page, by adding the following code to my Page_Load event handler:

protected void Page_Load(object sender, EventArgs e)
{
  PageScriptManager.Scripts.Add(
    new ScriptReference("~/Script/Ajax.Server.Transfer.Fixer.js")
    );
}

Problem solved and I didn't have to rewrite the entire application to get rid of Server.Transfer calls. On to other things.

Follow me on Mastodon!