ASP.NET 4.0 browser capabilities

The authors discuss how to build a custom browser capabilities provider and validate ASP.NET pages with W3C validator. Get a 40 percent discount on this book by clicking on the book image, and use promo code egghead40.

This article is based on ASP.NET 4.0 in Practice, by Daniele Bochicchio, Stefano Mostarda, and Marco De Sanctis, to be published January 2011. It is being reproduced here by permission from Manning Publications.
Manning early access books and ebooks are sold exclusively through Manning. Visit the book's page for more information.


The authors discuss how to build a custom browser capabilities provider and validate ASP.NET pages with W3C validator.

Browser capabilities influence the way control adapters are used by ASP.NET rendering infrastructure for both pages and controls. This feature is innate in ASP.NET and is based on a set of file definitions that basically includes the most common user agents from both standalone and mobile device browsers.

Time has shown that, for Microsoft, it is practically impossible to keep this list updated, so new alternatives
emerged. In order to maintain updated definitions, before ASP.NET 4.0 you had to do it manually. There are
different sources that are providing the definitions, and one of the most authoritative is certainly the definition
distributed at http://mdbf.codeplex.com/.

The current version of ASP.NET directly supports following browsers and platforms:

§ RIM’s Blackberry
§
Google Chrome
§ Mozilla FireFox
§ Internet Explorer
§
Internet Explorer Mobile
§ Apple’s iPhone
§
Opera
§
Apple’s Safari

In addition, generic profiles—one for search engine crawlers/spiders and a default one—are provided to cover the others platforms and browsers.

While the file definitions represent a useful way to instruct browser capabilities and adaptive rendering, it can
be better to control the way the capabilities are provided via code. Even if ".browser" files (like everything else in
ASP.NET) are converted in objects, controlling entirely the process can produce simpler results than editing or
updating those XML files. That is why ASP.NET 4.0 introduces the concept of browser capabilities providers.

Building a custom browser capabilities provider

ASP.NET browser capabilities providers can be used to totally replace the standard capabilities definition mechanism or to expand it by adding new functionalities.

By replacing the standard definition, you can, in fact, alter the way ASP.NET produces output.

Problem

One of the problems with the default file definitions is that they are not updated regularly so we want to bypass the
standard mechanism and provide a new one that will produce, for every request, the best markup it is possible to
produce.

Solution

ASP.NET 4.0 introduces a new class, named HttpCapabilitiesProvider. As its name suggests, this feature
implements the Provider Model pattern. Basically, you can define a provider to implement a specific behavior, using a base class as the interface to implement. This will guarantee that the providers will have the identical structure, so they can be defined in the configuration. The advantage in this case is that you do not need to write specific adapter or define file configurations, but you can express your own rules in code.

Specifically, to implement a custom engine, you have to overwrite the GetBrowserCapabilities method and provide a new instance of HttpBrowserCapabilities as result. Since this method will be called several times during the page lifecycle, you need to specify a caching pattern.

Listing 1 contains a very basic implementation of a provider.

Listing 1 The custom browser capabilities provider

C#

public class MyBrowserProvider : HttpCapabilitiesProvider
{

public override HttpBrowserCapabilities GetBrowserCapabilities(HttpRequest request)
{

string cacheKey = "MyBrowserProvider_"+ request.UserAgent??"empty"; #1 int cacheTimeout = 360;

HttpBrowserCapabilities browserCaps = HttpContext.Current.Cache[cacheKey] as
HttpBrowserCapabilities;

if (browserCaps == null)
{

browserCaps = new HttpBrowserCapabilities();

Hashtable values = new Hashtable(20, StringComparer.OrdinalIgnoreCase);

values["browser"] = request.UserAgent; #2

values["tables"] = "true";

values["supportsRedirectWithCookie"] = "true"; values["cookies"] = "true";

values["ecmascriptversion"] = "3.0";
values["w3cdomversion"] = "1.0";
values["jscriptversion"] = "6.0";

values["tagwriter"] = "System.Web.UI.HtmlTextWriter";

values["IsIPhone"] = ((request.UserAgent ?? string.Empty).IndexOf("iphone") > - 1).ToString(); #3

browserCaps.Capabilities = values;

HttpRuntime.Cache.Add(cacheKey, browserCaps, null,

DateTime.Now.AddSeconds(cacheTimeout), TimeSpan.Zero, CacheItemPriority.Low, null);
}

return browserCaps;

}

}

VB

Public Class MyBrowserProvider

Inherits HttpCapabilitiesProvider

Public Overloads Overrides Function GetBrowserCapabilities(ByVal request As HttpRequest) As
HttpBrowserCapabilities

Dim cacheKey As String = If("MyBrowserProvider_" & request.UserAgent, "empty")
#1

Dim cacheTimeout As Integer = 360

Dim browserCaps As HttpBrowserCapabilities = TryCast(HttpContext.Current.Cache(cacheKey),
HttpBrowserCapabilities)

If browserCaps Is Nothing Then

browserCaps = New HttpBrowserCapabilities()

Dim values As New Hashtable(20, StringComparer.OrdinalIgnoreCase)

values("browser") = request.UserAgent #2

values("tables") = "true"

values("supportsRedirectWithCookie") = "true" values("cookies") = "true"

values("ecmascriptversion") = "3.0"
values("w3cdomversion") = "1.0"
values("jscriptversion") = "6.0"

values("tagwriter") = "System.Web.UI.HtmlTextWriter"

values("IsIPhone") = ((If(request.UserAgent, String.Empty)).IndexOf("iphone") > -1).ToString() #3

browserCaps.Capabilities = values

HttpRuntime.Cache.Add(cacheKey, browserCaps, Nothing,

DateTime.Now.AddSeconds(cacheTimeout), TimeSpan.Zero, CacheItemPriority.Low, _
Nothing)

End If

Return browserCaps
End Function

End Class

#1 A unique key for user agent
#2 Standard capabilities
#3 Custom capabilities

As you can see in the previous listing, we are defining a set of capabilities. Those are the minimum you need to make the page work and instruct the server controls to use the most advanced markup and JavaScript code. Figure 1 contains the results of the default provider and of our custom provider.

Figure 1 The new provider populates the properties according to its code. You can see the default provider using IE 8.0 on the left and the custom on the right.

You can specify the provider to be in web.config using this code:

<configuration>
<system.web>
<browserCaps provider="ASPNET4InPractice.MyBrowserProvider, App_Code" />
</system.web>
</configuration>

Or, if you prefer, you can define the provider programmatically in global.asasx using the Application_Start
event (or using an equivalent HttpModule):

C#

void Application_Start(object sender, EventArgs e)
{

HttpCapabilitiesBase.BrowserCapabilitiesProvider = new MyBrowserProvider();

}

VB

Private Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
HttpCapabilitiesBase.BrowserCapabilitiesProvider =

New MyBrowserProvider()

End Sub

You can define both standard and nonstandard capabilities in your definition, and you can query them using a

similar syntax shown here:

<ul>

<li>IsMobile device: <%=Request.Browser.IsMobileDevice %></li>
<li>Platform: <%=Request.Browser.Platform %></li>
<li>IsIPhone: <%=(Request.Browser["IsIPhone"] as string)%></li>
</ul>

As noted, you can find the result in figure 1. The values should be expressed as a string, even if the capabilities are
then exposed as a
boolean. This is probably caused by the first implementation in ASP.NET 1.0, where text is
mandatory (as it was based on XML tags) and you need to address this problem if you want to make it working.

Discussion

Browser capabilities providers are really useful when you want to add new properties or create a new way to define the default capabilities. In its simpler form, a provider is composed of a few lines of code but, as you may note in case of the new IsIPhone property, you can also define new properties based on code evaluation.

This solution has no drawbacks because even the XML definition files are compiled, so you should not worry about performance.

Now that we are speaking of browser capabilities, the last scenario of this article will address a useful problem: how to validate your markup against the W3C validator service.

Validating ASP.NET pages with W3C validator

Adaptive rendering may be joy and pain. An example of the latter is certainly the absence of the W3C validator user agent from the recognized default browsers. This can be a problem if you want to validate your page’s markup, since the output generated for unknown browser is a conservative HTML 3.2.

Problem

If you try to validate the markup generated by an ASP.NET page with the W3C validator, you will probably have
troubles: ASP.NET does not recognize the user agent and serves the less advanced markup than it can handle—
HTML 3.2.

If you need to validate your markup, this presents a problem.

Solution

As previously noted, ASP.NET uses the browser capabilities to produce specific output for specific browsers. If you need to produce better markup by default, you can use the previous example, where a unique behavior is applied to all the requests.

If you do not want to override the default provider, you can define a custom file definition. The W3C Validator user agent contains "W3C_Validator" in the sent string so, in order to identify it, all you need is to produce a rule as in listing 2.

<browsers>

<browser id="W3C_Validator" parentID="default">
<identification>

<userAgent match="^W3C_Validator" /> #1

</identification>
<capabilities>

<capability name="browser" value="W3C Validator" />
<capability name="ecmaScriptVersion" value="1.2" />
<capability name="javascript" value="true" />
<capability name="supportsCss" value="true" />
<capability name="tables" value="true" />
<capability name="w3cdomversion" value="1.0" />

<capability name="tagWriter" value="System.Web.UI.HtmlTextWriter" /> #2

</capabilities>

</browser>
</browsers>

#1 A RegEx to intercept the browser
#2 The tag writer to be used

As you can see in listing 2, by specifying HtmlTextWriter instead of Html32TextWriter, you can produce XHTML/HTML 4.01 markup instead of HTML 3.2. The others properties will do the rest to enable DOM, JavaScript, and CSS support.

You can register it globally or locally by simply saving this file as w3c.browser in your \App_Browsers\ folder.

Discussion

ASP.NET 4.0 provides great flexibility in terms of markup generation and browser capabilities so you can leverage the new features to enrich your applications with less effort than in the past.

This last scenario is a great example of how you can add more features by simply understanding how the infrastructure works. Even if these scenarios will not fit in every application you will build, they may help you when more control will be needed.

Summary

Version 4.0 browser capabilities have new features, which let you completely substitute the entire engine to define your own set of definitions. By controlling browser capabilities you can produce better markup and put into practice a better web by implementing a correct support for web standards and promote accessibility. And you can boost even your old existing applications, moving them to ASP.NET 4.0.


By Peter Bromberg   Popularity  (6435 Views)