A while back, I decided I needed to add browser-specific capabilities to my web application. While there are those who advocate using capability testing rather than browser sniffing, there is at least one good reason to prefer sniffing. That is that you want to be sure your site works as well as possible in all browsers but you want to take advantage of capabilities only available in some.

In itself, this is not a reason to prefer sniffing; the key, however, is that you "want to be sure," which means testing; otherwise, your stuff may or may not work, which isn't very reassuring. If you don't have the resources to do testing in all target browsers or the time to develop Javascript workarounds based strictly on specific capability detection, then sniffing is a good alternative because it allows you to only use "advanced" functionality in the browsers that you have tested and fall back to standard functionality for the rest. This of course assumes you have architected your stuff in such a way as to make downgrading possible and still offer fairly equivalent services in a less rich presentation. That in itself can be challenging and is far too involved and a bit off topic for this post.

So let's just assume you can fall back. The next question is where you do the downgrading. You can do it in Javascript, which if you only want to alter some functionality on the client side is fine, but if you want to, say, avoid Javascript altogether or emit significantly different script based on browser, the choice is to detect on the server and act appropriately.

Thankfully, most web server technologies support browser sniffing, and ASP.NET has expanded on and improved on this with their control adapters in 2.0. But you can still use the old browser capabilities approach. To do this in 2.0, you simply add the special App_Browsers folder to your web site or project (if you're using WAP). In there you just add a file with the .browser extension, and you can put in your own custom browser capabilities there. Here's an example:

That's it, if all you want to do is extend the existing browser definitions. If you want to define new ones or to find out more about the browser schema, you can consult the MSDN docs. Using refID just lets you reference an existing definition and extend it. You can view existing definitions that ship with .NET in the Framework CONFIG\Browsers directory (e.g., C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG\Browsers), but be sure not to modify those directly to avoid your customizations being overwritten with later patches.

Note: You might be tempted to set a default value using refID="Default" as the docs suggest, but I've confirmed that there is a bug that causes the default to actually overwrite the more specific settings. Microsoft tells me that they have scheduled a fix for this bug and that it will be released with the next Service Pack, but if you need it sooner, you can create a support incident and get a hotfix. So the workaround is to not use Default and have your code check against null to determine default. It's not the nicest approach, especially when you're using a Boolean value that would be better to just parse to bool, but it's not a travesty either.

You can then test for the capability in your code like so:

this

.Page.Request.Browser["supportAjaxNavigation"] == "true"

and do your downgrading if need be. Of course, if you have some serious alternative rendering that needs to occur, you should consider using control adapters (especially the PageAdapter) to avoid complicating code with lots of conditional statements.