Extending ASP.NET MVC View Engine to Render Device Specific Views - Part One

In this series of posts, I will describe how we can use ASP.NET MVC framework's built-in features and extensibility to provide a better experience for users of various devices. In this post, we will look at how following conventions can help us quickly build multiple views and what benefits we might be getting from it. Later, we will improve our design by writing a custom view engine.

Background

Different websites have different ways of rendering the appropriate view for a given device. A common solution is to design websites responsively. But that is not always ideal as sometimes we need to render different views specifically designed for devices. Two very common practices are redirecting the user to a sub-domain (heraldsun website takes mobile users to http://m.heraldsun.com.au/) or a completely different domain (carsales takes you to carsales.mobi). A disadvantage of these approaches is that they inherently introduce multiple URL’s for the same page depending on the device. Another issue arises if you share http://m.heraldsun.com.au/ with someone, they will see the mobile page regardless of their device. That is bad user experience.

I think it would be better if each page in application has a single URL. The view rendered to the user will be different according to their device. We can achieve this by naming the views according to ASP.NET MVC conventions.

The action method below returns Index view with Master as masterpage/layout:

<h1>Hello World!</h1>  

All we need to do now is to create two versions for the view (and for the master page) and naming them according to ASP.NET MVC conventions: Index.cshtml and Index.Mobile.cshtml for the view and Master.cshtml and Master.Mobile.cshtml for master page (replace .cshtml with .aspx in all file names if using webform view engine).
Now let's run the project. It simply works! Index() action method returns Index.cshtml for desktop users and Index.Mobile.cshtml for mobile devices.

How does that work? Well, if the requesting device is a mobile, the view engine built into MVC will automatically look for [viewName].mobile.cshtml first and will return that one. If not found, it will revert back to the good old [viewName]. cshtml. This sounds very intuitive and to some degree magical. You can stop reading if that’s all you needed. Sometimes, however, that’s not enough.

Custom View Engine

Many device detection libraries (inlcuding 51 degrees), mark iPad as a mobile device. What if we have designed some iPad specific views and want them to be displayed for iPad devices? Well, there is a way. With ASP.NET MVC, there is always a way. Firstly, let’s create some iPad specific views:

Now it’s time to create a custom view engine.

class MyViewEngine : RazorViewEngine //or WebFormViewEngine  
{
  public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
  {
    ViewEngineResult result;
    if (controllerContext.HttpContext.Request.UserAgent.ToLower().Contains(“ipad”))
    {
      masterName = masterName + ".ipad";
      string viewPathAndName = viewName + ".ipad";
      //Mobile view retrieved
      result = base.FindView(controllerContext, viewPathAndName, masterName, false);
    }
    else
    {
      //full view retrieved
      result = base.FindView(controllerContext, viewName, masterName, false);
    }

  return result;
}

This custom view engine needs to be registered in Global.asax:

protected override void OnApplicationStarted(){  
  ViewEngines.Engines.Add(new MyViewEngine());
}

So if the requesting device is an iPad, the “[viewname].ipad” is retrieved. Otherwise, we fall back to the built-in view engine FindView method.

Summary

In this post, we looked at using ASP.NET MVC built-in capabilities to retrieve views based on the requesting device without changing the URL. We then customised the solution to cater for iPad specific views.

In the next post, I will describe an alternative method to renaming view names in an attempt to have more control and flexibility over the rendered views.