All in all, the biggest difference between ASP.NET MVC and ASP.NET Web Forms is the neat separation that exists in ASP.NET MVC between the actions that follows a request and the generation of the subsequent response for the browser.
The request lifecycle In Web Forms was a continuous flow. Firstly, a Page object was created from default settings hard-coded in the ASPX file and then initialized to the last known good state read from the viewstate field. The user code had then a chance to further update the state of the Page object before the postback event was handled and the state of the page to render back to the user was prepared.
All this happened in a single procedure: There was little chance for developers to serve different views in front of the same request. On the other hand, Web Forms is built around the concept of a “page”. If you request a page, you’re going to get “that” page. Subsequently, if you request default.aspx from a site intended for desktop use why should you be expecting to receive a mobile optimized page instead if you’re making the request from a mobile device? If you want a mobile page, you just set up a new mobile site and make it reachable through a different URL. At that point, you have a distinct “page” to invoke and it all works as expected.
Web Forms at some point was also extended with convention-based tricks to automatically serve different master pages to different browsers and also to make server controls capable of returning different values for different browsers. Nevertheless, Web Forms serves the vision of the web world that was mainstream more than a decade ago. Unless you have serious backward compatibility reasons, you should definitely consider making the step forward to ASP.NET MVC; and use ASP.NET MVC for any new development.
Anyway, this article is NOT about the endless debate the relative merits of Web Forms and MVC-there’s really nothing to discuss there. This article is about new features introduced in ASP.NET MVC 4 to make it really easy and effective to serve different views in front of the same request.
Display Modes
Here’s the classic example where display modes fit in. Suppose you have a Home controller with an Index method.
1 2 3 4 5 6 7 |
public class HomeController : Controller { public ActionResult Index() { return View(); } } |
As you know, you should also have a file named index.cshtml located under the Views/Home folder in the project. This file will provide the HTML for the browser. In the body of the Index method above you code (or better, you invoke from other components) the logic required to serve the request. If, by executing this piece of logic, data is produced which needs to be embedded in the view, then you pass this data down to the view object by adding an argument to the View method.
1 2 3 4 5 6 7 8 |
public class HomeController : Controller { public ActionResult Index() { var model = ProcessRequestAndGetData(); return View(model); } } |
So far so good.
Now in ASP.NET MVC 4 there’s an extra piece of magic that you might not know about yet. To experience the thrill of it, you add a new file to the Views/Home folder named index.mobile.cshtml. You can give this file any content you like; just make sure the content is different from the aforementioned index.cshtml.
Now launch the sample site and visit it with both a regular desktop browser, Internet Explorer perhaps, and a mobile browser. You can use the Windows Phone emulator or perhaps Opera Emulator. However, the simplest thing you can do to test the feature without much pain is to hit F12 and bring up the IE Developer’s Tools window. From there, you set a fake user agent that matches a mobile device. If you are clueless about what to enter, here’s a suggestion that matches an iPhone running iPhone OS 6:
1 |
Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) |
Quite surprisingly, the view you get for the same home/index URL is the mobile view as coded in the file index.mobile.cshtml.
What the heck is going on? Is this pure magic?
Even though I’m a firm believer that there can’t be any magic in software, well, I faced some terrible doubts until I found out about display modes.
Display Modes: Where Are Them?
To spot where display modes are and the role they play, I then used .NET Reflector to statically track the code path starting with the View method on the Controller class. From the View method, the code flow reaches the selected view engine-the RazorViewEngineclass in all cases in which CSHTML views are used. In ASP.NET MVC 4 all standard view engines inherit from the same base class-VirtualPathProviderViewEngine. This class has a new protected property named DisplayModeProvider. This property is of typeDisplayModeProvider. TheVirtualPathProviderViewEngine lists some helper methods through which the view name is resolved. The view engine receives the view name as set at the controller level: it can be name like “index” or it can be the empty string, as in the example above. If no view name is provided the view engine assumes it is the name of the action.
In ASP.NET MVC 4, an extra step takes place in theVirtualPathProviderViewEngine base class from which both WebFormsViewEngine and RazorViewEngine inherit. During the resolution of the view name, the view engine queries theDisplayModeProvider object to see if any of the registered display modes can be applied to the requested view. If a match is found, then the original view name is changed to point to the CSHTML file that represents the match. So, for instance, it may happen that “index” becomes “index.mobile“.
Let’s now explore further the internals of the DisplayModeProvider class.
The DisplayModeProvider Class
The documentation refers to this class as being internal to the framework; however, it has a few public members that you might, and should, be using in order to extend your site with multiple ad hoc views. The class has a static constructor that .NET Reflector decompiles as below:
1 2 3 4 5 6 7 |
static DisplayModeProvider() { MobileDisplayModeId = "Mobile"; DefaultDisplayModeId = string.Empty; _displayModeKey = new object(); _instance = new DisplayModeProvider(); } |
And here’s the constructor instead:
1 2 3 4 5 6 7 8 9 10 |
internal DisplayModeProvider() { List list = new List(); DefaultDisplayMode mode = new DefaultDisplayMode(MobileDisplayModeId) { ContextCondition = context => context.GetOverriddenBrowser().IsMobileDevice }; list.Add(mode); list.Add(new DefaultDisplayMode()); this._displayModes = list; } |
It turns out that DisplayModeProvider holds a list of DefaultDisplayMode objects each representing a display mode. By default, the provider holds two display modes: default and mobile. The default display mode is characterized by the empty string; the mobile display mode is characterized by the “mobile” string. These strings basically identify the suffix appended to the view name. This is where file name index.mobile.cshtml comes from.
It is interesting to focus on the following code:
1 2 3 |
DefaultDisplayMode mode = new DefaultDisplayMode(MobileDisplayModeId) { ContextCondition = context => context.GetOverriddenBrowser().IsMobileDevice }; |
In spite of a misleading name, the DefaultDisplayMode class is just the official class that represents a display mode. As I see things, the “Default” prefix in the name is just out of place. A display mode class is built around two main pieces of information: suffix name and matching rule. In the code snippet above, a new display mode class is created with the suffix of “mobile”-the actual value of the MobileDisplayModeIdfield-and a matching rule assigned to the ContextConditionproperty. Property ContextCondition is a delegate as below:
1 |
Func<HttpContextBase, Boolean> |
The purpose of the delegate is to analyze the HTTP context of the current request and return a Boolean answer to the question: should this display mode be used to serve the current request? How the Boolean response is found is entirely up to the implementation. As defined above, the mobile display mode parses the user agent string that comes with the request and seeks to find known keywords that would mark it for that of a mobile device. I’ll return on this point in a moment.
Listing Current Display Modes
You hardly have the need to do this in code, but I encourage you to try that out for pure fun. Here’s the code that reads and displays the currently available modes:
1 2 3 4 5 6 7 8 |
<ul> @{ foreach(var d in DisplayModeProvider.Instance.Modes) { <li>@(String.IsNullOrEmpty(d.DisplayModeId) ?"default" :d.DisplayModeId)</li> } } </ul> |
You use the Instance static member to access the singleton instance of the DisplayModeProvider class and flip through the Modes property. By the way, the getter of the Modes property just returns the value stored in the internal _displayModes field dissected earlier through .NET Reflector.
Beyond the Default Configuration
The default and mobile display modes come free out of the box, but honestly they are not worth the cost. I have two reasons to say this. First, modern web sites need more than just a mobile/desktop dichotomy for views. You might want to distinguish tablets, smartphones, legacy phones, perhaps smart TVs. Sometimes this can be achieved with CSS media queries; sometimes you need to do server-side detection of the device via its provided user agent string. This leads to the second reason I have to blissfully ignore the default ASP.NET MVC configuration. Even if a plain desktop/mobile dichotomy works for your site, the point is that the logic behind the mobile context condition is weak and flaky. It has good chances to work with iPhone and BlackBerry devices; it may not even work with Windows Phone and Android devices-let alone with older and simpler devices. The method IsMobileDevice you have seen referenced a while back does sniffing of the user agent string based on the information it can find in the following .browser files you get installed with ASP.NET.
The model is clearly extensible and you can add more information at any time; but writing a .browser file may not be easy and the burden of testing, checking, and further extending the database is entirely on your shoulders.
The figure proves that I added a fairly large (18 MB) browser file-an XML file actually-named mobile.browser. That file comes from an old Microsoft project now discontinued and contains a reasonable amount of devices as of summer of 2011. All devices and browsers which came next are not correctly detected.
In the end, display modes are an excellent piece of infrastructure but require a bit of work on your end for configuration and additional tools to do view routing work effectively. The siren call about ASP.NET MVC being fully mobile aware is just a siren call.
What Can You Do About It?
You use display modes to give your site multiple views in front of the same URL. More concretely, this mostly means using defining a display mode for each device, or class of devices, you’re interested in. You could create an iPhone display mode for example. Likewise, you could create a tablet display mode. Here’s some code:
1 2 3 4 5 6 7 8 9 10 11 |
var modeTablet = new DefaultDisplayMode("tablet") { ContextCondition = (c => IsTablet(c.Request)) }; var modeDesktop = new DefaultDisplayMode("") { ContextCondition = (c => return true) }; displayModes.Clear(); displayModes.Add(modeTablet); displayModes.Add(modeDesktop); |
When run from Application_Start, the code drops default modes and defines two new modes: tablet and desktop. The tablet mode is added first and will be checked first. The internal logic that finds the appropriate display mode, in fact, stops at first match. If the HTTP request is not matched to a tablet, it is then treated by default with a view optimized for a desktop device.
How would you reliably determine whether a given request comes from a tablet? It’s all about sniffing the user agent string; but you need a professional tool for that. The answer is getting a commercial license from a Device Description Repository vendor like ScientiaMobile for WURFL. Note that WURFL is only the most widely used (Facebook and Google use it) device database; it is free for open source projects and has a partly free cloud version. Other products exist too. But my point here is that you should not spend a second on crafting your own solution for sniffing user agent strings.
Summary
In my opinion, display modes are the best reason for making the jump to ASP.NET MVC 4-even more than web API, whose benefit can be easily achieved also in MVC 3. Display modes are mostly for mobile sites even though a view is just a view and display modes just give you a free engine to route view based on HTTP context-based custom rules. Have a look, enjoy, and come back here with your thoughts!
Load comments