Blog Archives

Use ASP.NET’s HttpHandler to bridge the cross-domain gap

When you’re developing client-side applications, a problem you’ll almost inevitably have to deal with is how to work with services that reside outside your website’s domain. Though many modern APIs do support JSONP, which is a clever workaround to somewhat mitigate the cross-domain problem, JSONP has its own problems.

Worse, if you encounter an API with no JSONP support, the cross-domain barrier can quickly become a formidable one. CORS is slowly becoming a viable alternative, but it requires that the remote service support it via special HTTP headers and browser support for CORS is still not ubiquitous.

Until CORS is more broadly supported, an alternative solution is to bounce cross-domain requests through the web server that hosts your website. In ASP.NET, the best tool for implementing that sort of middleman endpoint is the HttpHandler.

In this post, I’ll show you how to create an HttpHandler to service cross-domain requests, how to use jQuery to communicate with the handler, and an example of one improvement that this approach makes possible.

An example remote API

To focus on an example that’s already familiar to many, I’m going to use Twitter. Twitter’s API does support JSONP, which is a viable alternative for consuming it across domains. In fact, the Twitter status that you see in my sidebar to the right was retrieved from Twitter’s API via JSONP.

However, not every service supports JSONP, its third-party script injection mechanism is sometimes problematic, and using JSONP robs us of niceties like local caching. So, for the sake of a good example, let’s find a way to use the Twitter API on the client-side without resorting to JSONP.

Specifically, I’m interested in querying the service for my last few status updates. The Twitter API request to accomplish that looks like this:

Twitter will respond to that with a JSON array of objects representing my (or your) last 20 tweets, which is exactly what we’re after.

The best tool for the job: HttpHandler

If you’re accustomed to using ASP.NET’s page methods and ScriptServices to facilitate communication between client and server, those tools begin to look like a hammer that matches every JSON-shaped nail in sight. However, when simply relaying an external API’s JSON through to the client, they often add unnecessary overhead and complexity.

Rather, a lower-level tool is more appropriate in this case.

HttpHandlers are one of ASP.NET’s most under-utilized tools. They’re simple to implement and allow you to handle requests closer to the metal than WebForms pages or MVC controller actions.

One place in particular where HttpHandlers shine is where you would otherwise consider writing Response.Write statements in a WebForms page’s code-behind. This anti-pattern of using ASPX’s code-behind to get closer to the metal looks similar to approaches that you’ll see on some other platforms, such as PHP, but is not equivalent.

Unfortunately, even if you don’t use WebForms controls or ASPX markup at all, executing that low-level code from an ASPX page’s code-behind requires that every request filter through the full page life cycle. That means even the simplest request still has to percolate all the way from PreInit to Unload, adding needless overhead.

Instead, the HttpHandler is where you should write that sort of code that ultimately boils down to Response.Write calls.

Choosing the right handler type

A tricky issue when you’re writing your first HttpHandler is that Visual Studio presents you with two templates, “ASP.NET Handler” and “Generic Handler”:

The add item dialog presents two choices of HttpHandler templates

Both are similar, but the “ASP.NET Handler” template’s approach requires modifying your web.config to configure which URL your handler accepts requests at. Mucking around in the web.config isn’t terribly difficult, but it’s extra friction which makes the process less approachable.

In the spirit of keeping things simple, let’s stick with the more traditionally file-based “Generic Handler”.

Getting started with your first HttpHandler

After choosing that template, specifying a name, and adding the new file to your site, you’ll end up with a bit of boilerplate code that includes this method:

public void ProcessRequest(HttpContext context) {
  context.Response.ContentType = "text/plain";
  context.Response.Write("Hello World");
}

If you start the site up in Visual Studio and then request your newly-created HttpHandler in a browser, Handler1.ashx if you accept the default name, you will see “Hello World” as you might expect.

That’s not very impressive yet, but the response you saw made its way to your browser without touching WebForms’ page life cycle or filtering through ASP.NET MVC’s routing engine and action filters. While those things are worthwhile niceties for the majority of your application, they’re unwanted overhead when all you need is to efficiently relay some content through the server.

Bouncing a request to Twitter through the HttpHandler

To adapt an HttpHandler for relaying requests to the Twitter API, we can use .NET’s handy WebClient class to make the request to Twitter’s API, and then return the result back through as the handler’s response:

public void ProcessRequest(HttpContext context) {
  WebClient twitter = new WebClient();
 
  // The base URL for Twitter API requests.
  string baseUrl = "http://api.twitter.com/1/";
 
  // The specific API call that we're interested in.
  string request = "statuses/user_timeline.json?id=Encosia";
 
  // Make a request to the API and capture its result.
  string response = twitter.DownloadString(baseUrl + request);
 
  // Set the content-type so that libraries like jQuery can 
  //  automatically parse the result.
  context.Response.ContentType = "application/json";
 
  // Relay the API response back down to the client.
  context.Response.Write(response);
}

That code simply makes an HTTP request to the Twitter API and blindly bounces the result back through as the HttpHandler’s response. For the time being, everything is hard-coded, but we’ll improve on that soon enough.

Using the handler proxy on the client-side

With our web server doing the heavy lifting, using this server-side proxy to make a remote request is trivial:

$.getJSON('TwitterProxy.ashx', function(tweets) {
  // Call a magical function that does all the presentational work.
  displayTweets(tweets);
});

The jQuery code here is actually identical what you’d use when requesting the API via JSONP. Whether that response is truly being fulfilled by the specified URL or it’s being relayed through our HttpHandler, it’s all the same to the jQuery code on the client-side.

As you’ll see when we add caching, this can easily be exploited for good.

Mixing things up with QueryString parameters

The hard-coded approach works well enough, but what if we wanted to be able to query any Twitter account’s recent updates instead of being limited to just that boring Encosia character?

Since HttpHandlers receive an instance of the current HttpContext as the parameter to their ProcessRequest method, it’s easy to access QueryString parameters and react accordingly. For example, this would allow us to request any Twitter account’s timeline by via an id parameter on the QueryString:

public void ProcessRequest(HttpContext context) {
  WebClient twitter = new WebClient();
 
  string baseUrl = "http://api.twitter.com/1/";
 
  // Extract the desired account ID from the QueryString.
  string id = context.Request.QueryString["id"];
 
  // Make a request to the API for the specified id.
  string request = "statuses/user_timeline.json?id=" + id;
 
  // Same as before, from here on out:
  string response = twitter.DownloadString(baseUrl + request);
 
  context.Response.ContentType = "application/json";
  context.Response.Write(response);
}

Now it works exactly the same way as before, but we can choose which Twitter account’s timeline is requested. For example, this URL would request Scott Guthrie‘s latest tweets:

TwitterProxy.ashx?id=ScottGu

Supplying parameters with $.getJSON

To pass this new parameter in from the client-side, you could handcraft the entire URL including the appropriate QueryString. Even better though, $.getJSON has an optional “data” argument that accepts a JavaScript object and converts it to QueryString parameters:

$.getJSON('TwitterProxy.ashx', { id: 'ScottGu' }, function(tweets) {
  displayTweets(tweets);
});

jQuery will automatically URLEncode the parameters you specify in the “data” argument and properly assemble them into the final URL to be requested:

Screenshot of the HttpHandler request generated by the jQuery code above.

Which is exactly what we need it to do.

Using a configuration object like this is cleaner than manually concatenating a string together and makes it easier to vary the parameter at runtime.

Improving performance with server-side caching

An advantage the HttpHandler proxy has over CORS and JSONP is that you can perform any arbitrary server-side processing that you wish, both before and after the remote service repsonds. A great way to take advantage of that is adding a server-side caching layer.

Server-side caching will reduce how often requests actually trigger API calls and can significantly improve performance for requests that are already cached. A caching middleman like this is especially valuable when dealing with rate-limited APIs like Twitter’s.

Let’s say that we wanted to cache Twitter responses for up to five minutes, for example:

public void ProcessRequest(HttpContext context) {
  // This will be the case whether there's a cache hit or not.
  context.Response.ContentType = "application/json";
 
  // Check to see if the twitter status is already cached,
  //   then retrieve and return the cached value if so.
  // 8/3/11: Updated with more robust test, thanks to ctolkien.
  object tweetsCache = context.Cache["tweets-" + id];
 
  if (tweetsCache != null) {
    string cachedTweets = tweetsCache.ToString();
 
    context.Response.Write(cachedTweets);
 
    // We're done here.
    return;
  }
 
  WebClient twitter = new WebClient();
 
  // Move along; nothing to see here. The concatenation is just
  //  to avoid horizontal scrolling within the meager 492
  //  pixels I have to work with here.
  string url = "http://api.twitter.com/1/statuses/" +
               "user_timeline.json?id=Encosia";
 
  string tweets = twitter.DownloadString(url);
 
  // This monstrosity essentially just caches the WebClient result
  //  with a maximum lifetime of 5 minutes from now.
  // If you don't care about the expiration, this can be a simple
  //  context.Cache["tweets"] = tweets; instead.
  context.Cache.Add("tweets", tweets,
    null, DateTime.Now.AddMinutes(5),
    System.Web.Caching.Cache.NoSlidingExpiration,
    System.Web.Caching.CacheItemPriority.Normal,
    null);
 
  context.Response.Write(tweets);
}

Adding the intermediate cache results in a tremendous performance improvement after the first request:

Screenshot of an initial uncached request to Twitter and then the subsequent, cached requests

With the server able to immediately serve requests within the five minute caching window, subsequent $.getJSON requests are an order of magnitude faster!

Perhaps even more importantly in the case of Twitter, these four refreshes only counted as one API call against my hourly rate-limit.

Conclusion

Using HttpHandlers as server-side proxies turns out to be a simple way to solve the pesky cross-domain restrictions that we’ve all run into from time to time. All said and done, using an HttpHandler to proxy third-party requests takes few lines of code, but offers nearly unlimited flexibility.

In addition to the obvious benefit of getting around the cross-domain restriction, bouncing requests through your own server potentially has a range of other benefits, including:

  • Error handling – This approach not only passes unhandled exceptions on the WebClient request back through to the client-side, but it also gives you the ability to enhance the error handling with your own sanity checks and constraints.
  • Caching – As shown in this post’s final example, you can very easily interject your own caching layer for requests passing through the HttpHandler proxy. That’s especially useful when working against rate limited or potentially slow/flaky APIs (like Twitter’s).
  • Security – When you’re accustomed to server-side programming, the revealing nature of client-side JavaScript can be unnerving. Learning to appropriately partition sensitive algorithms and data between client and server is key to mitigating that issue. Along those lines, moving the remote request to code running on your server is one way to keep sensitive information like API keys and passwords safely hidden from view-source and client-side developer tools.
  • Reliability – One of JSONP’s less obvious drawbacks is the fact that it relies on injecting a third-party script. However, your users may be using something like NoScript to purposely block third-party scripts, effectively shutting down your ability to use JSONP. Even if you prefer JSONP in most cases, a local server-side proxy can be helpful as a fallback in case of unexpected JSONP failures.

That’s not to say that there’s no downsides to this approach. When you’re using an HttpHandler proxy, it’s important to keep in mind that it can be slower since you’re making a series of two connections instead of a single, direct one. You also lose the ability to request content with the user’s third-party cookies attached to the request, which is helpful in some cases.

Overall, using server-side proxies is a very useful item to have in your toolbox. I hope this post has served to introduce you to the approach and/or given you better insight into how you can use HttpHandlers to your advantage.

Get the source

If you’d like to browse through a complete working example of what’s been covered in this post, take a look at the companion project at GitHub. Or, if you’d like to download the entire project and run it in Visual Studio to see it in action yourself, grab the ZIP archive.

HttpHandler-Proxy on GitHubHttpHandler-Proxy.zip

Related posts:

  1. Save yourself some typing when you call ASP.NET services
  2. 3 mistakes to avoid when using jQuery with ASP.NET AJAX
  3. jQuery 1.5?s AJAX rewrite and ASP.NET services: All is well

You’ve been reading Use ASP.NET’s HttpHandler to bridge the cross-domain gap, originally posted at Encosia. I hope you enjoyed it, and thanks for reading.

If you’ve got any feedback, please click through and leave a comment; I’d love to hear from you. You can click here to jump directly to the comment section of this post.

Use ASP.NET’s HttpHandler to bridge the cross-domain gap

When you’re developing client-side applications, a problem you’ll almost inevitably have to deal with is how to work with services that reside outside your website’s domain. Though many modern APIs do support JSONP, which is a clever workaround to somewhat mitigate the cross-domain problem, JSONP has its own problems.

Worse, if you encounter an API with no JSONP support, the cross-domain barrier can quickly become a formidable one. CORS is slowly becoming a viable alternative, but it requires that the remote service support it via special HTTP headers and browser support for CORS is still not ubiquitous.

Until CORS is more broadly supported, an alternative solution is to bounce cross-domain requests through the web server that hosts your website. In ASP.NET, the best tool for implementing that sort of middleman endpoint is the HttpHandler.

In this post, I’ll show you how to create an HttpHandler to service cross-domain requests, how to use jQuery to communicate with the handler, and an example of one improvement that this approach makes possible.

An example remote API

To focus on an example that’s already familiar to many, I’m going to use Twitter. Twitter’s API does support JSONP, which is a viable alternative for consuming it across domains. In fact, the Twitter status that you see in my sidebar to the right was retrieved from Twitter’s API via JSONP.

However, not every service supports JSONP, its third-party script injection mechanism is sometimes problematic, and using JSONP robs us of niceties like local caching. So, for the sake of a good example, let’s find a way to use the Twitter API on the client-side without resorting to JSONP.

Specifically, I’m interested in querying the service for my last few status updates. The Twitter API request to accomplish that looks like this:

Twitter will respond to that with a JSON array of objects representing my (or your) last 20 tweets, which is exactly what we’re after.

The best tool for the job: HttpHandler

If you’re accustomed to using ASP.NET’s page methods and ScriptServices to facilitate communication between client and server, those tools begin to look like a hammer that matches every JSON-shaped nail in sight. However, when simply relaying an external API’s JSON through to the client, they often add unnecessary overhead and complexity.

Rather, a lower-level tool is more appropriate in this case.

HttpHandlers are one of ASP.NET’s most under-utilized tools. They’re simple to implement and allow you to handle requests closer to the metal than WebForms pages or MVC controller actions.

One place in particular where HttpHandlers shine is where you would otherwise consider writing Response.Write statements in a WebForms page’s code-behind. This anti-pattern of using ASPX’s code-behind to get closer to the metal looks similar to approaches that you’ll see on some other platforms, such as PHP, but is not equivalent.

Unfortunately, even if you don’t use WebForms controls or ASPX markup at all, executing that low-level code from an ASPX page’s code-behind requires that every request filter through the full page life cycle. That means even the simplest request still has to percolate all the way from PreInit to Unload, adding needless overhead.

Instead, the HttpHandler is where you should write that sort of code that ultimately boils down to Response.Write calls.

Choosing the right handler type

A tricky issue when you’re writing your first HttpHandler is that Visual Studio presents you with two templates, “ASP.NET Handler” and “Generic Handler”:

The add item dialog presents two choices of HttpHandler templates

Both are similar, but the “ASP.NET Handler” template’s approach requires modifying your web.config to configure which URL your handler accepts requests at. Mucking around in the web.config isn’t terribly difficult, but it’s extra friction which makes the process less approachable.

In the spirit of keeping things simple, let’s stick with the more traditionally file-based “Generic Handler”.

Getting started with your first HttpHandler

After choosing that template, specifying a name, and adding the new file to your site, you’ll end up with a bit of boilerplate code that includes this method:

public void ProcessRequest(HttpContext context) {
  context.Response.ContentType = "text/plain";
  context.Response.Write("Hello World");
}

If you start the site up in Visual Studio and then request your newly-created HttpHandler in a browser, Handler1.ashx if you accept the default name, you will see “Hello World” as you might expect.

That’s not very impressive yet, but the response you saw made its way to your browser without touching WebForms’ page life cycle or filtering through ASP.NET MVC’s routing engine and action filters. While those things are worthwhile niceties for the majority of your application, they’re unwanted overhead when all you need is to efficiently relay some content through the server.

Bouncing a request to Twitter through the HttpHandler

To adapt an HttpHandler for relaying requests to the Twitter API, we can use .NET’s handy WebClient class to make the request to Twitter’s API, and then return the result back through as the handler’s response:

public void ProcessRequest(HttpContext context) {
  WebClient twitter = new WebClient();
 
  // The base URL for Twitter API requests.
  string baseUrl = "http://api.twitter.com/1/";
 
  // The specific API call that we're interested in.
  string request = "statuses/user_timeline.json?id=Encosia";
 
  // Make a request to the API and capture its result.
  string response = twitter.DownloadString(baseUrl + request);
 
  // Set the content-type so that libraries like jQuery can 
  //  automatically parse the result.
  context.Response.ContentType = "application/json";
 
  // Relay the API response back down to the client.
  context.Response.Write(response);
}

That code simply makes an HTTP request to the Twitter API and blindly bounces the result back through as the HttpHandler’s response. For the time being, everything is hard-coded, but we’ll improve on that soon enough.

Using the handler proxy on the client-side

With our web server doing the heavy lifting, using this server-side proxy to make a remote request is trivial:

$.getJSON('TwitterProxy.ashx', function(tweets) {
  // Call a magical function that does all the presentational work.
  displayTweets(tweets);
});

The jQuery code here is actually identical what you’d use when requesting the API via JSONP. Whether that response is truly being fulfilled by the specified URL or it’s being relayed through our HttpHandler, it’s all the same to the jQuery code on the client-side.

As you’ll see when we add caching, this can easily be exploited for good.

Mixing things up with QueryString parameters

The hard-coded approach works well enough, but what if we wanted to be able to query any Twitter account’s recent updates instead of being limited to just that boring Encosia character?

Since HttpHandlers receive an instance of the current HttpContext as the parameter to their ProcessRequest method, it’s easy to access QueryString parameters and react accordingly. For example, this would allow us to request any Twitter account’s timeline by via an id parameter on the QueryString:

public void ProcessRequest(HttpContext context) {
  WebClient twitter = new WebClient();
 
  string baseUrl = "http://api.twitter.com/1/";
 
  // Extract the desired account ID from the QueryString.
  string id = context.Request.QueryString["id"];
 
  // Make a request to the API for the specified id.
  string request = "statuses/user_timeline.json?id=" + id;
 
  // Same as before, from here on out:
  string response = twitter.DownloadString(baseUrl + request);
 
  context.Response.ContentType = "application/json";
  context.Response.Write(response);
}

Now it works exactly the same way as before, but we can choose which Twitter account’s timeline is requested. For example, this URL would request Scott Guthrie‘s latest tweets:

TwitterProxy.ashx?id=ScottGu

Supplying parameters with $.getJSON

To pass this new parameter in from the client-side, you could handcraft the entire URL including the appropriate QueryString. Even better though, $.getJSON has an optional “data” argument that accepts a JavaScript object and converts it to QueryString parameters:

$.getJSON('TwitterProxy.ashx', { id: 'ScottGu' }, function(tweets) {
  displayTweets(tweets);
});

jQuery will automatically URLEncode the parameters you specify in the “data” argument and properly assemble them into the final URL to be requested:

Screenshot of the HttpHandler request generated by the jQuery code above.

Which is exactly what we need it to do.

Using a configuration object like this is cleaner than manually concatenating a string together and makes it easier to vary the parameter at runtime.

Improving performance with server-side caching

An advantage the HttpHandler proxy has over CORS and JSONP is that you can perform any arbitrary server-side processing that you wish, both before and after the remote service repsonds. A great way to take advantage of that is adding a server-side caching layer.

Server-side caching will reduce how often requests actually trigger API calls and can significantly improve performance for requests that are already cached. A caching middleman like this is especially valuable when dealing with rate-limited APIs like Twitter’s.

Let’s say that we wanted to cache Twitter responses for up to five minutes, for example:

public void ProcessRequest(HttpContext context) {
  // This will be the case whether there's a cache hit or not.
  context.Response.ContentType = "application/json";
 
  // Check to see if the twitter status is already cached,
  //   then retrieve and return the cached value if so.
  // 8/3/11: Updated with more robust test, thanks to ctolkien.
  object tweetsCache = context.Cache["tweets-" + id];
 
  if (tweetsCache != null) {
    string cachedTweets = tweetsCache.ToString();
 
    context.Response.Write(cachedTweets);
 
    // We're done here.
    return;
  }
 
  WebClient twitter = new WebClient();
 
  // Move along; nothing to see here. The concatenation is just
  //  to avoid horizontal scrolling within the meager 492
  //  pixels I have to work with here.
  string url = "http://api.twitter.com/1/statuses/" +
               "user_timeline.json?id=Encosia";
 
  string tweets = twitter.DownloadString(url);
 
  // This monstrosity essentially just caches the WebClient result
  //  with a maximum lifetime of 5 minutes from now.
  // If you don't care about the expiration, this can be a simple
  //  context.Cache["tweets"] = tweets; instead.
  context.Cache.Add("tweets", tweets,
    null, DateTime.Now.AddMinutes(5),
    System.Web.Caching.Cache.NoSlidingExpiration,
    System.Web.Caching.CacheItemPriority.Normal,
    null);
 
  context.Response.Write(tweets);
}

Adding the intermediate cache results in a tremendous performance improvement after the first request:

Screenshot of an initial uncached request to Twitter and then the subsequent, cached requests

With the server able to immediately serve requests within the five minute caching window, subsequent $.getJSON requests are an order of magnitude faster!

Perhaps even more importantly in the case of Twitter, these four refreshes only counted as one API call against my hourly rate-limit.

Conclusion

Using HttpHandlers as server-side proxies turns out to be a simple way to solve the pesky cross-domain restrictions that we’ve all run into from time to time. All said and done, using an HttpHandler to proxy third-party requests takes few lines of code, but offers nearly unlimited flexibility.

In addition to the obvious benefit of getting around the cross-domain restriction, bouncing requests through your own server potentially has a range of other benefits, including:

  • Error handling – This approach not only passes unhandled exceptions on the WebClient request back through to the client-side, but it also gives you the ability to enhance the error handling with your own sanity checks and constraints.
  • Caching – As shown in this post’s final example, you can very easily interject your own caching layer for requests passing through the HttpHandler proxy. That’s especially useful when working against rate limited or potentially slow/flaky APIs (like Twitter’s).
  • Security – When you’re accustomed to server-side programming, the revealing nature of client-side JavaScript can be unnerving. Learning to appropriately partition sensitive algorithms and data between client and server is key to mitigating that issue. Along those lines, moving the remote request to code running on your server is one way to keep sensitive information like API keys and passwords safely hidden from view-source and client-side developer tools.
  • Reliability – One of JSONP’s less obvious drawbacks is the fact that it relies on injecting a third-party script. However, your users may be using something like NoScript to purposely block third-party scripts, effectively shutting down your ability to use JSONP. Even if you prefer JSONP in most cases, a local server-side proxy can be helpful as a fallback in case of unexpected JSONP failures.

That’s not to say that there’s no downsides to this approach. When you’re using an HttpHandler proxy, it’s important to keep in mind that it can be slower since you’re making a series of two connections instead of a single, direct one. You also lose the ability to request content with the user’s third-party cookies attached to the request, which is helpful in some cases.

Overall, using server-side proxies is a very useful item to have in your toolbox. I hope this post has served to introduce you to the approach and/or given you better insight into how you can use HttpHandlers to your advantage.

Get the source

If you’d like to browse through a complete working example of what’s been covered in this post, take a look at the companion project at GitHub. Or, if you’d like to download the entire project and run it in Visual Studio to see it in action yourself, grab the ZIP archive.

HttpHandler-Proxy on GitHubHttpHandler-Proxy.zip

Related posts:

  1. AJAX, file downloads, and IFRAMEs
  2. The easiest way to break ASP.NET AJAX pages
  3. Why ASP.NET AJAX UpdatePanels are dangerous

You’ve been reading Use ASP.NET’s HttpHandler to bridge the cross-domain gap, originally posted at Encosia. I hope you enjoyed it, and thanks for reading.

If you’ve got any feedback, please click through and leave a comment; I’d love to hear from you. You can click here to jump directly to the comment section of this post.


You’ve been reading Use ASP.NET’s HttpHandler to bridge the cross-domain gap, originally posted at Encosia. I hope you enjoyed it, and thanks for reading.

Prepare web.config for HTML5 and CSS3

HTML5 and CSS3 introduces some new file types that enables us to create even better websites. We are now able to embed video, audio and custom fonts natively to any web page. Some of these file types are relatively new and not supported by the IIS web server by default. It’s file types like .m4v, .webm and .woff.

When a request is made to the IIS for these unsupported file types, we are met with the following error message:

HTTP Error 404.3 – Not Found

The page you are requesting cannot be served because of the extension configuration. If the page is a script, add a handler. If the file should be downloaded, add a MIME map.

The problem is that the IIS doesn’t know how to serve these new files unless we tell it how. This can be easily done in the web.config’s <system.webServer> section by adding the following snippet:

<staticContent>
    <mimeMap fileExtension=".mp4" mimeType="video/mp4" />
    <mimeMap fileExtension=".m4v" mimeType="video/m4v" />
    <mimeMap fileExtension=".ogg" mimeType="video/ogg" />
    <mimeMap fileExtension=".ogv" mimeType="video/ogg" />
    <mimeMap fileExtension=".webm" mimeType="video/webm" />

    <mimeMap fileExtension=".oga" mimeType="audio/ogg" />
    <mimeMap fileExtension=".spx" mimeType="audio/ogg" />

    <mimeMap fileExtension=".svg" mimeType="images/svg+xml" />
    <mimeMap fileExtension=".svgz" mimeType="images/svg+xml" />

    <remove fileExtension=".eot" />
    <mimeMap fileExtension=".eot" mimeType="application/vnd.ms-fontobject" />
    <mimeMap fileExtension=".otf" mimeType="font/otf" />
    <mimeMap fileExtension=".woff" mimeType="font/x-woff" />
</staticContent>

The above snippet includes support for most video, audio and font file types used by HTML5 and CSS3.

Performance tuning tricks for ASP.NET and IIS 7 – part 2

In part 1 of this series, we looked at some tricks to optimize the performance of any website running in IIS 7 by only modifying the web.config. In this part we will focus on handling browser caching issues and optimize the number of JavaScript and CSS files loaded from an ASP.NET website.

NB! All the code (a single .cs file of 125 lines) is included in the zip file at the bottom of this post.

Browser caching

In part 1, we looked at how it was possible to set an expiration header to any static file such as JavaScript and CSS files, so the browser would cache them for a long time and thereby optimize both for bandwidth and the number of requested files going from server to browser.

The problem with setting a browser cache expiration date of i.e. a JavaScript file to a year in the future becomes clear when you change the file before it expires in your visitor’s browsers. They simply won’t see the changes until they either clear their cache or hits F5 manually.

Adding the version number

The only viable way to maintain a far-in-the-future expiration date is to change the URL of the file when the file changes. So instead of including script files like so:

<script type="text/javascript" src=”/scripts/global.js"></script>

…we really want to get a version number included in the src attribute, like so:

<script type="text/javascript" src=/scripts/v_634174870689341736/global.js"></script>

The problem with this is that ASP.NET doesn’t have any feature that will inject a version number, so we have to create that our selves. It is very simple to do so by looking at when the file was last changed and then retrieve the ticks from that date. In the zip file below you’ll find a method that does exactly that and it can be used like so:

<script type="text/javascript" src="<%=BundleHelper.InsertFile("/scripts/global.js") %>"></script>

The BundleHelper.InsertFile method is one you want to use for Stylesheets as well, like so:

<link rel="Stylesheet" href="<%=BundleHelper.InsertFile("includes/style.css") %>" type="text/css" />

Ok, now all our JavaScript and stylesheet references have the version number in the path. Next thing to look at is getting it working with the updated non-existing path.

The HTTP handler

To be able to serve the correct file even with the version number in the path, we need to register an HTTP handler in the web.config’s <system.webServer> section like so:

<add name="ScriptBundler" verb="GET,HEAD" path="*.js" type="FileBundleHandler" />
<add name="CssBundler" verb="GET,HEAD" path="*.css" type="FileBundleHandler" />

The handler we just registered is called FileBundleHandler and knows how to filter out the version number to find the right file. It supports both .css and .js files. The handler also makes sure to both output cache and browser cache correctly. Just add the FileBundleHandler.cs file from the zip file to your website and you are up and running.

Now the browser cache issue has been resolved by adding a version number to the path of the included file and by adding an HTTP handler that knows how to remove it again when serving the file.

Bundle multiple files

Another common website performance issue is that there are many JavaScript and CSS files included on a page. This scenario results in the browser have to download a lot of extra files and that all slows down the performance of a website. The solution to this is also very simple when you’ve first completed the above steps to register the HTTP handler in web.config and called the BundleHelper.InsertFile method when inserting JavaScript and CSS files.

The folder structure convention

There are many ways of bundling files into a single request, like Justin Etheredge’s Squisher. For this example I have chosen a convention based approach because that doesn’t require any code to implement.

Any given ASP.NET website might have a folder structure similar to this:

The folder convention supported in the FileBundleHandler lets you reference a folder instead of just a file. Both the HTTP handler and the BundleHelper.InsertFile understand when a folder is referenced and automatically bundles all the .js or .css files to a single response. So in order to bundle all the files in a given folder, simply reference the folder name and add the extension of the types of files you want bundled. Having the folder structure above, you can add a bundle like so:

<script type="text/javascript" src="<%=BundleHelper.InsertFile("/scripts/common.js") %>"></script>

Notice that the file /scripts/common.js doesn’t exist, but the folder /scripts/common does. By adding .js at the end, we tell the HTTP handler to look for all files with the same file extension – in this case .js files. It bundles all the files in alphabetical order and serve the as a single response. For security reasons, the HTTP handler will only serve .css and .js extensions.

Minification

Since we are now running all JavaScript and stylesheet files in bundles and through the HTTP handler, it makes sense to also look at the content of the files to optimize even further.

For this example I’m using the Microsoft Ajax Minifier (MAM), which is a single .dll file capable of minifying both JavaScript and stylesheets. The MAM is my favorite JavaScript minifier since it not only removes whitespace, it also rewrites variable and function names and a lot of other things as well. For me it has proven a better choice than the YUI Compressor and Google Closure Compiler. The stylesheet minifier feature of MAM also looks very nice, but I have honestly never used it before except for this example.

Basically what MAM does is that it optimizes and removes unwanted whitespace from both JavaScript and stylesheets. The HTTP handler makes use of MAM for both single files and bundled ones, so you get full benefit no matter your scenario.

Summary

No matter if you use the website model, the web application model or ASP.NET MVC you are now able to utilize the browser cache to the fullest. Furthermore, by bundling your files using the folder convention you can minimize the number of requests sent by the browser. Both JavaScript and stylesheet files are also minified and optimized for even smaller file sizes sent over the wire.

It’s worth noticing that the output caching respects file changes and therefore refreshes evertime changes are made to the JavaScript and CSS files tunnelled through this code.

Following the techniques in part 1 combined with this example will improve any website’s server-to-browser performance substantially.

Implementation

  1. Download the zip file below and place the AjaxMin.dll in your bin folder.
  2. Then place the FileBundleHandler.cs in your App_Code folder if you use the website model – otherwise place it where ever it makes sense in your structure.
  3. Now register the HTTP handler in your web.config under the <system.webServer> section like so:

    <add name="ScriptBundler" verb="GET,HEAD" path="*.js" type="FileBundleHandler" />
    <add name="CssBundler" verb="GET,HEAD" path="*.css" type="FileBundleHandler" />

  4. The last thing you need is to start using the BundleHelper.InsertFile method on your pages for both JavaScript and stylesheets like so:

    <script type="text/javascript" src="<%=BundleHelper.InsertFile("/scripts/common.js") %>"></script>
    <link rel="Stylesheet" href="<%=BundleHelper.InsertFile("styles/global.css") %>" type="text/css" />

Download

FileBundler.zip (89,95 kb)

ServerInfo – Easily scan a Machine/Server Farm for Information

new open-source project in Asp.Net MVC 2

More than once I have been asked what databases are on what server, if server X is running application Y or what websites server ABC is running. These are are relatively simple things to accomplish, and there are tools available to get this information, but this is an extremely simple and portable solution – all the data is kept in xml, so there is no need to install a backend.

All that it is required to get all this information is to enter ip addresses and the user has the rights to scan the machines requested.

In addition, this can keep track of all of the owners of the machines and has a GUI for running WMI Queries, which is extremely powerful if you know how to use it.

This is written with Asp.Net MVC 2, C# and xml; it requires .Net 4.0 framework. I will be updating this to MVC 3/Razor in the near future.


using Server.MapPath() in an Generic Handler (.ashx)

if you need to get a local file path in your ashx

Instead of using:

Server.MapPath()

Simply use:

System.Web.HttpContext.Current.Server.MapPath()


Inline AJAX DropDown and Text Editing with Asp.Net MVC and jQuery

including how to use a database to populate the dropdown


First thing is first, you will need to download
jQuery
and the
Jeditable
plugin (I prefer to refer to it as the Jedi-Table!).
Be sure to put these references in your View (or Masterpage).
Next, you have to set up a view on which to use an inline edit.
I find that I often want to use this approach on tables of information.
For this View, I will set it to use an IEnumerable of an Item I have called ‘ItemOwner’ (this is arbitrary and does not really matter).
It will be a simple table that lists the Name and the Country of the owner, both of which will be editable inline.
Here is the Index in my ExampleController.cs:

myDataContext db = new myDataContext();
public ActionResult Index()
{
    // get the info for the 'Countries' dropdown:
    ViewData["countries"] = db.Countries
        .Select(x => new SelectListItem()
        {
            Text = x.Name,
            Value = x.Id.ToString()
        }).ToJson();

    // get the 'ItemOwners' I am interested in:
    var owners = db.ItemOwners.Take(3);

    return View(owners);
}

As you can see there, I am also pulling the countries from the database and throwing them into the ViewState – we will get to this later.
Since the Country is actually a foreign key relation, the value is set to an integer which is the identity field in the database.
It is also using a .ToJson() extension which takes a IEnumerable<SelectListItem> and
puts it into a simple JSON string that I use which is here:

public static string
    ToJson(this IEnumerable<SelectListItem> slis)
{
    string output = "{";
    if (slis != null)
    {
        for (int i = 0; i < slis.Count(); i++)
        {
            output += " '" + slis.Skip(i)
            .First().Value + "': '" +
            slis.Skip(i).First().Text + "'" +
            (i == slis.Count() - 1 ? " " : ",");
        }
    }
    return output += "}";
}

There is probably a better way to do that… but I don’t know it?!

I am also pulling 3 ItemOwners from the database, I know this is silly, but it just an example.
Here is how I am displaying them in the view:

<table>
    <thead>
        <tr>
            <th>Name</th>
            <th>Country</th>
        </tr>
    </thead>
    <tbody>
        <% foreach(var owner in Model) { %>
        <tr>
            <td><%= owner.Name %></td>
            <td><%= owner.Country.Abbreviation %></td>
        </tr>
        <% } %>
    </tbody>
</table>

Now that there is a simple table we want to make it a bit more interactive.
Since we aregoing to make all of these fields editable, we need to add in a way to distinguish exactly what they are.
To do that, we will need two things: the id of the item they are editing, and the type of inline editing we will be doing (i.e. dropdown or text input).
So to do that, let’s add in a few css classes and an identifieng ID:

<td id="name<%= owner.Id %>" class="editable text">
    <%= owner.Name %></td>
<td id="ctry<%= owner.Id %>" class="editable dropdown">
    <%= owner.Country.Abbreviation %></td>


And now add a little css to make them appear to be clickable:

td.editable:hover
{ cursor:pointer; background-color:Orange; }

Now they all look like you can click on them, so we can move on to making the click actually do something.



This is where the jQuery comes in, and it is very simple.
I have made these ‘helper’ methods in Javascript to make all of my inline calls centrally controllable, I keep this in my sites script folder so if I change one inline edit, I change them all; it also makes for more readable Javascript on each page.

function InlineDropdown(collectionToDropDown, ajaxAddress, dropDownDataSet) {
    collectionToDropDown.editable(ajaxAddress,
    {
        data: dropDownDataSet,
        type: 'select',
        indicator: 'saving...',
        tooltip: 'click to edit...',
        submit: 'Save',
        style: 'inherit',
        placeholder: 'click to edit'
    });
}

function InlineTextbox(collectionToInline, ajaxAddress) {
    collectionToInline.editable(ajaxAddress,
    {
        indicator: 'saving...',
        tooltip: 'click to edit...',
        style: 'inherit',
        placeholder: 'click to edit'
    });
}

function InlineTextarea(collectionToInline, ajaxAddress) {
    collectionToInline.editable(ajaxAddress,
    {
        type        : 'textarea',
        rows        : 4,
        indicator   : 'saving...',
        tooltip     : 'click to edit...',
        style       : 'inherit',
        submit      : 'Save',
        onblur      : 'ignore',
        placeholder : 'click to edit'
    });
}

Obviously you can read all about the options on the Jeditable
page, but this is how I set them.
Also notice I have a InineTextarea included as well for a textarea which is not covered here but works the exact same.

Now the jQuery calls are almost trivial:

InlineTextbox(
    $('td.editable.text'),
    "<%= Url.Content("~/Ajax/ItemOwner.ashx") %>"
);

InlineDropdown(
    $('td.editable.dropdown'),
    "<%= Url.Content("~/Ajax/ItemOwner.ashx") %>",
    <%= ViewData["countries"].ToString() %>
);

What that is doing is sending the POST requests to the specified address.
The POST contains a few things:

  • id – the id of the element that sent the request
  • value – the new value passed by the element

We are also passing more information there – remember that we passed both the type of field to edit and the id of the ItemOwner to edit, ie [name837] which emans we want to edit the Name field of ItemOwner 837.
So we simply set up an ashx handler (which we specified above) to do the dirty work:

public void ProcessRequest(HttpContext context)
{
    string newValue;
    try
    {
        myDataContext db = new myDataContext();
        string elementId = context.Request.Form["id"];

        // since we made the first 4 of the id the 'field' whic to edit
        // we can just pull the first 4 letters for use in our switch:
        string fieldToEdit = elementId.Substring(0, 4);

        //now take anything after those 4 and it is the Id:
        int idToEdit = Convert.ToInt32(elementId.Remove(0, 4));

        // the value is simply a string:
        newValue = context.Request.Form["value"].Trim();

        // now that we have the id, get the ItemOwner from the db
        ItemOwner owner = db.ItemOwners.FirstOrDefault(x => x.Id == idToEdit);

        // after all is said and done, we will return newValue to the user so the field
        // looks as if the change has taken place (which it has)

        // using the field we pulled above, decide what to do:
        switch (fieldToEdit)
        {
            // name is easy
            case "name": owner.Name = newValue; break;

            // since the country is an integer foreign key, we need to Convert.ToInt32:
            case "ctry":
                owner.CountryId = Convert.ToInt32(newValue);
                // now that we have recorded the value, we want to return the text to
                // the user and not the id value which would make no sense
                newValue = db.Countries.FirstOrDefault(x => x.Id == owner.CountryId).Abbreviation;
                break;
            // if it wasn't caught, something is wrong:
            default: throw new Exception("invalid fieldToEdit passed");
        }

        db.SubmitChanges(); // save it
    }
    // now if an exceptions were reported, the user can see what happened
    // this also inform the user nothing was saved
    // you could easily make this not reported to the user and logged elsewhere
    catch (Exception ex)
    { newValue = "Error: " + ex.Message + " [nothing written to db]"; }

    //now return what you want in the element:
    context.Response.Write(newValue);
}

And that is all it takes.

Performance tuning tricks for ASP.NET and IIS 7 – part 1

In this first installment of performance tuning tricks for ASP.NET and IIS 7 we will look at some of the easy, yet powerful possibilities in the web.config file. By taking advantage of these few tricks we can increase the performance of any new or existing website without changing anything but the web.config file.

The following XML snippets must be placed in the <system.webServer> section of the web.config.

HTTP compression

You’ve always been able to perform HTTP compression in ASP.NET by using third-party libraries or own custom built ones. With IIS 7 you can now throw that away and utilize the build-in compression available from the web.config. Add the following line to enable HTTP compression:

<urlCompression doDynamicCompression="true" doStaticCompression="true" dynamicCompressionBeforeCache="true"/>

By default, only text based content types are compressed.

doDynamicCompression

Setting this attribute to true enables compression of dynamically generated content such as pages, views, handlers. There really aren’t any reasons not to enable this.

doStaticCompression

This attribute allows you to decide whether or not you want static files such as stylesheets and script files to be compressed. Images and other non-text content types will not be compressed by default. This is also something you want to enable.

dynamicCompressionBeforeCache

If you do output caching from within your ASP.NET website, you can tell IIS 7 to compress the output before putting it into cache. Only if you do some custom output caching you might run into issues with setting this to true. Try it and test it. If your website works with this enabled, then you definitely want to keep it enabled.

Tip

By default, only text based content types are compressed. That means if you send application/x-javascript as content type, you should change it to text/javascript. If you use some custom modules in your website, then you might experience conflicts with the IIS 7 compression feature.

Resources

Cache static files

To speed up the load time for the visitors, it is crucial that everything that can be cached by the browser IS cached by the browser. That includes static files such as images, stylesheets and script files. By letting the browser cache all these files means it doesn’t need to request them again for the duration of the cache period. That saves you and your visitors a lot of bandwidth and makes the page load faster. A well primed browser cache also triggers the load and DOMContentLoaded event sooner.

By adding this snippet to your web.config, all static files are cached in the browser for 1 year:

<staticContent>
     <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="365.00:00:00"/>
</staticContent>

This setting sets the expiration date of the file one year in the future. It does that by setting an HTTP header that instruct the browser to add the file to its internal cache. If you hit F5 or ctrl-F5, the browser will request the files no matter what the expiration is set to.

A major problem with client-side caching is if your static files change before the cache expires. Then the visitor with the old version in the cache won’t see the new file until she clears the browser cache or hit F5. Therefore, this setting must be used with caution and probably with a shorter expiration time. In part 2 of this series I’ll address this problem and provide a simple solution to it.

Tip

Make sure that user sensitive information isn’t cached on the browser. It will then be available by anyone else using the same browser.

Resources

Using an iPhone with the Visual Studio development server

Testing an ASP.NET site on an iPhone Developing iPhone-optimized portions of an ASP.NET website presents a challenge. More specifically, it’s testing your creations that can be difficult.

Apple’s iPhone emulator only runs on Macs and the Windows-based alternatives don’t emulate mobile Safari well. That leaves us using an actual device as the only high-fidelity option for testing. That’s not all bad; especially when it comes to a touch-driven interface, testing with the real thing is preferable.

Unfortunately, the ASP.NET Development Server bundled with Visual Studio is severely restricted when it comes to testing externally. In fact, it could hardly be more restrictive – it refuses all external connections, even if those connections originate from the same local subnet.

In this post, I’m going to show you one way I’ve found to circumvent that restriction, how to configure your iPhone to take advantage of that, and how to connect to the development server once those steps are completed.

Note: This post specifically describes configuring an iPhone, but the same approach will work for any mobile device that supports using an HTTP proxy.

Fooling the ASP.NET Development Server

The fundamental problem is that Visual Studio’s ASP.NET Development Server actively refuses external connections. That’s a logical precaution if you’re in the business of selling web server operating systems, but it adds unnecessary friction to the legitimate endeavor of testing with mobile devices.

The solution that I stumbled onto uses a tool that you may already have installed: Fiddler. If you aren’t familiar with Fiddler, this recording of Eric Lawrence’s session at MIX10 is a great way to learn a lot about Fiddler in relatively little time.

The feature that we’re specifically interested in is its HTTP proxy server. Unlike the ASP.NET Development Server, Fiddler does not restrict connections from external devices. Even better, routing an external device’s connections through Fiddler is misdirection enough to fool the development server into accepting them.

Checking Fiddler’s proxy port

With Fiddler installed, the first step is to determine which port it’s running the proxy server on. On fresh installs, the default setting is port 8888.

If you’ve had Fiddler installed a while, it doesn’t hurt to double check the setting. You can do that in Fiddler by navigating to Tools > Fiddler Options, and selecting the Connections tab:

Finding the port that Fiddler's proxy server listens on

While you have that dialog open, also verify that the three checkboxes circled above are checked.

Finding your IP address

The next step is to determine your machine’s IP address on the local network. A quick way to do that is running ipconfig at the command prompt.

To open a command prompt, press Win + R, type cmd in the field, and hit enter.

Running cmd

At the command prompt that opens, type ipconfig and hit enter.

Finding the local IP address with ipconfig

What you’re looking for here is the IPv4 address for your machine’s primary network adapter. “Local Area Connection” is mine, so I need to use 192.168.1.119 to connect to my machine. A wireless connection is fine too, as long as it’s connected to the same access point that the iPhone is.

Find yours and make note of it for the next step.

Note: This must be an IP address that your iPhone can route to while connected via Wi-Fi. In most business and almost all residential networks, you won’t need to give this much thought. However, if you’re working within a more complex corporate network and can’t get your iPhone to connect to Fiddler’s proxy server, you may need help from a system administrator.

Configuring an iPhone to route through Fiddler

With your development machine’s IP address and Fiddler’s port number in hand, you’re ready to configure your iPhone to channel its network traffic through Fiddler’s proxy server.

To do that, open the settings app and tap the Wi-Fi option (below, left).

Navigating through the iPhone's settings

In the Wi-Fi Networks panel (above, right), you’ll see the wireless networks that your iPhone has detected in range. Tap the arrow at the right side of the Wi-Fi connection that you intend to use for testing.

Setting the iPhone's proxy settingsAt the very bottom of the panel that opens (left), find the HTTP Proxy setting and tap Manual (1) to enable the feature. In the fields that appear, enter your computer’s local IP address for the server (2), and the port that Fiddler is listening on for the… Port (3).

That’s it! Your iPhone is configured to route its traffic through an instance of Fiddler running on your development machine.

Starting the development server

Now that you have a conduit from your iPhone to the development server, it’s time to get the development server running by starting your site in Visual Studio. Anything that starts an instance of the development server will do (e.g. Start Without Debugging or View in Browser).

Make note of the URL displayed in your browser when Visual Studio displays your website. We’ll modify that slightly in the next step and use it to access the development server from Mobile Safari.

If the development server is already running, you can also determine its address by right-clicking its icon in the system tray and choosing “Show Details”. That will present you with a window that looks like this:

Finding the port and virtual root of your development server app

The “Root URL” address there is what you’ll need in the final step.

Accessing the development server from your device

Finally, we’re ready to start testing against the development server from the browser on a mobile device. The one minor issue remaining is that the exact URL advertised by the development server won’t work in this setup.

To make Fiddler happy, you need to append a trailing period to the hostname portion of the address. For instance, this “Root URL” advertised in the example above will not work without modification:

Wrong!

localhost:24833/WebSite1

To make it work, we simply need to append the trailing period to localhost:

localhost.:24833/WebSite1

That does look odd, but it works.

Fiddler also recognizes ipv4.fiddler as an alias for the localhost loopback, which is a little bit more intuitive. So, you could also access the same example with this address if you prefer:

ipv4.fiddler:24833/WebSite1

That’s it. You’re armed and ready to test with any external device on your local network now, so long as it supports routing its traffic through an HTTP proxy.

Conclusion

At first, this may seem like many steps and a lot of work. Don’t worry. Once you go through the motions a few times, you’ll find that it’s a breeze.

It’s especially smooth sailing in future repetitions, since your machine’s local IP probably won’t change often, and Fiddler’s proxy IP won’t change at all.

Of course, Fiddler isn’t the only utility that will work as an intermediary like this, but using Fiddler brings the great side-effect of providing HTTP traffic analysis while you’re testing. That added utility is welcome when you’re testing on a mobile device where the on-device development tools are basically nonexistent.

###

Originally posted at Encosia. If you’re reading this elsewhere, come on over and see the original.

Using an iPhone with the Visual Studio development server

ASMX ScriptService mistake – Invalid JSON primitive

One group of searches that consistently brings traffic here is variations on the error: Invalid JSON primitive. Unfortunately, the post that Google sends that traffic to doesn’t address the issue until somewhere within its 150+ comments.

Today, the topic gets its own post.

If you’ve worked with ASMX ScriptServices or Page Methods without ASP.NET AJAX’s client-side proxy (e.g. using jQuery or pure XMLHttpRequest code), you’ve may have seen this cryptic error yourself. Or, perhaps you’ve just arrived here due to seeing it for the first time.

Either way, you may be surprised to learn that the most common reason for this error is that you’ve lied to ASP.NET during your AJAX request.

It all begins with the Content-Type

HTTP’s Content-Type header is a fundamental aspect of communication between browsers and servers, yet often remains hidden from us in day-to-day development. The Content-Type header allows an HTTP connection to describe the format of its contents, using Internet media types (also known as MIME types). A few common ones that you’ve probably seen before are text/html, image/png, and the more topical application/json.

Without the flexible negotiation process content types provide, your users’ browsers and your version of IIS would have to both be “ASMX Compatible” and “JSON Compatible” in order for ScriptServices to function. What a nightmare that would be! The IE6 difficulties we face today would pale in comparison.

Further, Content-Type negotiation is part of what allows a single URL, such as WebService.asmx, to represent data in more than one format (e.g. XML and JSON in ASMX’s case).

The benefits of Content-Type negotiation are well worth a bit of occasional hassle.

Okay, but why does that matter?

When your browser sends a POST request, the W3C’s recommendation is that it should default to using a Content-Type of application/x-www-form-urlencoded. The HTML 4.01 spec describes that serialization scheme:

This is the default content type. Forms submitted with this content type must be encoded as follows:

  1. [Omitted for brevity; not relevant to this post.]
  2. The control names/values are listed in the order they appear in the document. The name is separated from the value by ‘=’ and name/value pairs are separated from each other by ‘&’.

For an example of what that means, consider this simple form:

<form method="post">
  <label>First Name</label>
  <input id="FirstName" value="Dave" name="FirstName" />
 
  <label>Last Name</label>
  <input id="LastName" value="Ward" name="LastName" />
</form>

When the preceding form is submitted with URL encoded serialization, the request’s POST data will look like this:

Firebug screenshot showing the URLEncoded POST data

That standardized serialization format allows a server-side backend like ASP.NET to decipher a form submission’s contents and give you access to each key/value pair. Regardless of what sort of browser submits a form to the server, the Content-Type facilitates a predictable conversion from POST data to server-side collection.

In other words, the Content-Type corresponds to a serialization scheme.

What does that have to do with JSON Primitives?

Understanding Content-Type negotiation and how it relates to serialization is important due to its role in coaxing JSON out of ASMX ScriptServices. Specifically, the fact that you must set a Content-Type of application/json on the request means you’re instructing ASP.NET to interpret your input parameters as JSON serialized data.

However, the W3C’s mandate of URL encoding by default means that most AJAX libraries default to that serialization scheme. Similarly, AJAX tutorials targeting endpoints other than ASMX ScriptServices (including even ASP.NET MVC examples) will describe sending URL encoded data to the server.

In other words, when you’re working with a client-side object like this:

var Person = { FirstName: 'Dave',
               LastName:  'Ward' }

The default serialization scheme makes it easy to inadvertently transmit that data to the server as a URL encoded string:

FirstName=Dave&LastName=Ward

Again, remember that a Content-Type of application/json is a requirement when working with ASMX ScriptServices. By setting that Content-Type on the request, you’ve committed to sending JSON serialized parameters, and a URL encoded string is far from valid JSON.

In fact, it’s invalid JSON (primitive?), hence the cryptic error message.

Instead of the URL encoded string above, you must be sure to send a JSON string:

{'FirstName':'Dave','LastName':'Ward'}

Whether you’re using XMLHttpRequest directly or a JavaScript library that abstracts the details, getting your request’s serialization wrong is the root of the invalid JSON primitive error. However, a more specific issue tends to be the leading cause of this happening.

When good JavaScript libraries go bad

The most common source of this error stems from a subtlety of using jQuery’s $.ajax() method to call ASMX ScriptServices. Cobbling together snippets of code from the documentation, platform agnostic tutorials, and even posts here on my site, it’s easy to end up with something like this:

// WRONG!
$.ajax({
  type: 'POST',
  contentType: 'application/json',
  dataType: 'json',
  url: 'WebService.asmx/Hello',
  data: { FirstName: "Dave", LastName: "Ward" }
});

Notice the JavaScript object literal being supplied to $.ajax()’s data parameter. That appears vaguely correct, but will result in the invalid JSON primitive error.

Why? jQuery serializes $.ajax()’s data parameter using the URL encoded scheme, regardless of what Content-Type is specified. Even though the contentType parameter clearly specifies JSON serialization, this URL encoded string is what jQuery will send to the server:

FirstName=Dave&LastName=Ward

That obviously isn’t valid JSON!

The solution is as simple as two single-quotes:

// RIGHT
$.ajax({
  type: 'POST',
  contentType: 'application/json',
  dataType: 'json',
  url: 'WebService.asmx/Hello',
  data: '{ FirstName: "Dave", LastName: "Ward" }'
});

Did you spot the difference?

Instead of a JavaScript object literal, the data parameter is a JSON string now. The difference is subtle, but helpful to understand. Since it’s a string, jQuery won’t attempt to perform any further transformation, and the JSON string will be unimpeded as it is passed to the ASMX ScriptService.

It doesn’t have to be this way

The problem is trivial once you’re aware of the underlying issue, but there’s not a great reason I can see why things need to be this way in the first place. Either half of this equation could easily provide a remedy.

jQuery – I believe the most correct solution would be $.ajax() attempting to honor the serialization scheme indicated by its contentType parameter. In the case of application/json fixing this could be easy as testing for JSON.stringify and using it if available, to avoid adding any complexity/size to jQuery core.

That would leave it our responsibility to reference a copy of json2.js in older browsers, but that convention wouldn’t be much of a burden. We generally do that anyway when the client-side objects get complex.

Microsoft – It’s absolutely correct that the framework throws an error when you lie to it about what you’re sending. However, a bit of leniency could potentially save thousands of hours spent troubleshooting this problem (if my search traffic is any indication of its prevalence).

Is there any reason that the ScriptHandlerFactory can’t intelligently differentiate between between JSON and URL encoded inputs? If the first non-whitespace character of the request isn’t an opening curly brace, why not attempt to deserialize it as URL encoded before throwing an invalid JSON primitive error?

###

Originally posted at Encosia. If you’re reading this elsewhere, come on over and see the original.

ASMX ScriptService mistake – Invalid JSON primitive

WP Like Button Plugin by Free WordPress Templates