Friday, November 21, 2008

Low-tech AJAX requests with jQuery and ASP.NET 3.5

ASP.NET has all kinds of fancy features for out-of-band ajax communication. There's web services, script methods, page methods and what not. I was looking for an easier, low profile method that will also work without the Microsoft's AJAX framework and doesn't require proxy generation by ScriptHandlerFactory. The goal is to be able to do some AJAX stuff with a jQuery.

MVC leads the way
So, looking at the MVC Beta, I noticed there's a JsonResult class. The gut of the class does nothing more than this:

Response.ContentType = "application/json";
JavaScriptSerializer serializer = new JavaScriptSerializer();
Response.Write(serializer.Serialize(data));
Now, that's what I call easy. JavaScriptSerializer is in the System.Web.Extensions assembly, so that's commonly available in ASP.NET 3.5 and not part of the MVC framework. Great, that means that from a basic ASP.NET 3.5 application we can do something like this with jQuery: $.getJSON("Default.aspx", dataReady); This will trigger a post to the ASP.NET page. The dataReady function will be invoked when the request returns. But how do we know whether to return JSON data or normal web content? One option is to append a parameter to the url (for example ajax=true). A nicer solution is to check the headers. jQuery will set the Accept header to indicate it expects a JSON result: Accept: application/json, text/javascript, */* Implementation
Now, by overriding the Render method in the page it's relatively easy to handle both the web requests and the ajax requests:
protected override void Render( HtmlTextWriter writer )
{ 
  if ( Request.AcceptTypes.Contains( "application/json", StringComparer.OrdinalIgnoreCase ) )
  {
    Response.ContentType = quot;application/json";
    JavaScriptSerializer serializer = new JavaScriptSerializer();
    Response.Write( serializer.Serialize( data ) );
  }
  else
  {
    base.Render( writer );
  } 
}
JSON trouble
As it turns out, there is just one snag here. ASP.NET and jQuery apparently don't speak the exact same dialect of JSON. This basically means that DateTime values wont be turned into JavaScript Date objects on the client. We can fix that by moving this solution to the server:
JavaScriptSerializer serializer = new JavaScriptSerializer();
Response.Write( System.Text.RegularExpressions.Regex.Replace( serializer.Serialize( data ), @"\""\\\/(Date\([0-9-]+\))\\\/\""", "new $1" ) );
Additional considerations
Please note that this is just a proof of concept; in a real life application you should impose all kinds of security measures before responding to an AJAX request. You may also want to split the page and the handling of the AJAX callbacks into separate classes. Handling the AJAX request through an IHttpHandler a lot more efficient.