Что такое Mobile Features в ASP.NET MVC 4
ASP.NET MVC 4 вышел с очень ожидаемой и долгожданной новой возможностью — то, что по английски называют Mobile Features — поддержкой мобильных устройств. По большому счету, название сразу удивило, так как немного расходится с техническим описанием нововведения. Но промоушен — есть промоушен. Все упоминания про шаблоны страниц (layout) с поддержкой HTML 5, указание viewport, CSS media — все это к MVC фреймворку непосредственно отношения не имеет.
К новой возможности самого фреймворка можно отнести только два пункта:
- создание View и шаблонов специфичных для мобильных браузеров
- возможность на лету изменять то, какое View будет рендерится пользователю — а именно переключаться в рантайме между различными View
И здесь сразу стоит заметить, что слово «мобильный» упоминается исключительно в рекламных целях. MVC 4 позволяет создавать несколько View для каждой из требуемых страниц и определять для какого браузера каждая из них должна показываться. То есть, возможности ни коем образом не ограничиваются именно мобильными браузерами.
Все гениальное просто — «hook» во ViewEngine
Реализация данной возможности очень проста. Это просто «hook» во ViewEngine.
Вы делаете разные страницы (Views) для разных устройств или браузеров. Именуете файлы добавляя ключевое слово:
- Catalog.cshtml
- Catalog.iPhone.cshtml
Задаете критерий по которому будет выбираться нужная View — пользуясь DisplayModeProvider задаете нужные DefaultDisplayMode с нужным именем.
DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("iPhone")
{
ContextCondition = (context => context.GetOverriddenUserAgent().IndexOf
("iPhone", StringComparison.OrdinalIgnoreCase) >= 0)
});
В рантайме, когда отработает метод контроллера, подготовленный ViewResult передается во View Engine, проверяются условия DisplayModeProvider-a, он изменяет имя файла View, которое нужно загрузить.
Можем ли мы этим реально пользоваться?
Можем конечно. Но только в очень простых случаях. Основное неудобство именно в том, что все View, которые вы делаете для страницы обрабатываются одним и тем же методом контроллера. Рано или поздно, это становится проблемой.
Уже настало время, когда веб-сайт должен поддерживать браузеры не только на десктопных компьютерах, ноутбуках и мобильных устройствах, но еще и на телевизорах. Причем к мобильным устройствам относятся обычные телефоны, смартфоны и планшетные компьютеры. И согласитесь — если внешний вид на планшете и смартфоне еще может быть одинаковым, с одними и теми же JavaScript виджетами, то для обычных сотовых телефонов, коих еще много, такое же представление и такие же возможности Web UI обеспечить сложно.
Поддержка одновременно смартфонов и планшетов с первого взгляда выглядит не такой сложной — разница в размере экрана. Но на большей площади можно разместить больше информации или больше пользовательских функций, а не просто увеличить размер картинок.
Поддержка телевизоров выглядит очень простой задачей. Full HD означает одинаковый размер экрана в пикселах. Но на этом плюсы заканчиваются.
Оказывается, если Ваш сайт открыли с телевизора, он мог быть открыт в браузерах на:
- телевизоре Phillips Smart TV — NetTV SDK — сайт на CE-HTML
- телевизоре Samsung Smart TV — у Самсунга свое SDK, возможности сильно отличаются от NetTV
- телевизоре LG Smart TV
- с приставки XBOX 360 — Internet Explorer
- с приставки PlayStation 3
- с приставки Wii
- с Google TV, телевизоры Sony со встроенным Google TV
- с Apple TV
- Boxee box, Kylo browser, Roku и другие
Сделать версию сайта одинаково удобную для всех этих устройств мало реалистично. Если вы хотите воспользоваться всеми дополнительными возможностями, которые эти устройства предоставляют Вам как разработчикам — то делать это в одной версии сайта становится не рационально. А некоторые возможности, поверьте, стоят того, чтобы ими пользоваться.
Например, на XBOX работает обычный Internet Explorer, ширина экрана вполне достаточная для работы с обычной десктопной версией сайта, но пользователь вынужден на джойстике возить по экрану курсор. Для того, чтобы нажать на кнопку на экране ему требуется навести на нее курсор и нажать кнопку на джойстике. Становится очевидным, что более подходящий интерфейс — это интерфейс для планшетов, с большими прямоугольными кнопками. Комбобоксы также не удобны. Пролистывать страницу на джойстике достаточно удобно.
На SmartTV Вам придется расчитывать на то, что нет курсора и пользователь бегает по кнопкам и инпутам на странице нажимая кнопки вверх/вниз/влево/вправо на пульте. Чтобы поддерживать другие кнопки на пульте, которые упрощают навигацию, придется прочитать документацию из SDK и писать JavaScript специфичный для конретного устройства — key codes разные.
И это только если начать рассматривать телевизоры. Со смартфонами и планшетами также много интересного, но это более распространенная и часто обсуждаемая тема.
Стоит начать работать в этом направлении и очень быстро встает вопрос о том, что версии сайтов для десктопов, мобильных устройств и телевизоров должны быть различными. И различаться должны не только во View. Должны быть разные модели, а значит лучше всего, когда с ними работают разные методы контроллеров.
Что можно предложить взамен?
Понятно, что самое простое — сразу сделать разные сайты для разных устройств и редиректить на нужный. Откинем такое решение как слишком простое. К этому варианту можно откатиться в любой момент.
Можно конечно делать для разных устройств разные методы в одном и том же контроллере, но хотелось бы более удобного и красивого решения.
Учитывая архитектуру ASP.NET MVC 4, есть только один подходящий вариант. Внутри MVC приложения (проекта), каждый из вариантов сайта должен быть выполнен в своей Area. По User Agent String мы может определить от какого устройства/браузера поступает запрос и направить его в нужную Area.
Чтобы переправить в нужную Area нужно для кажого маршрута (Route) в таблице рутинга добавить ограничение (Constraint).
Минус такого подхода в том, что такое ограничение нужно не забыть добавить во все маршруты.
Например, если мы сделали Area в которую нужно перенаправлять всех с Chrome browser, то Area Registration будет выглядеть так:
public class ChromeAreaRegistration : AreaRegistration
{
public override string AreaName
{
get { return "Chrome"; }
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Chrome_default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new { isChromeBrowser = new IsChromeConstraint() }
);
}
}
При этом ограничение будет выглядеть так:
public class IsChromeConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
string userAgent = httpContext.Request.UserAgent;
if (string.IsNullOrWhiteSpace(userAgent)) { return false; }
return userAgent.Contains("Chrome");
}
}
При этом у нас есть основная Area, куда должны направляться все остальные браузеры, кроме Хрома.
public class DesktopAreaRegistration : AreaRegistration
{
public override string AreaName
{
get { return "Desktop"; }
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Desktop_default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new { isNotChromeBrowser = new IsNotChromeConstraint() }
);
}
}
в ней мы также должны использовать ограничение, которое фильтрует браузеры отсекая Хром:
public class IsNotChromeConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
string userAgent = httpContext.Request.UserAgent;
if (string.IsNullOrWhiteSpace(userAgent)) { return false; }
return !userAgent.Contains("Chrome");
}
}
Выставлять ограничения для всех маршрутов во всех Area необходимо, несмотря на то, что маршруты в рантайме просматриваются сверху вниз. Дело в том, что порядок регистрации AreaRegistration при старте случайный. Этого можно избежать, если регистрировать маршруты всех Area в одном месте — в Global.asax — явно указывая их порядок. Тогда можно гарантировать, что проверка на Хром браузер всегда будет происходить первой.
Постскриптум
Если Вам это надоест, вы всегда можете легко вынести каждую Area в отдельное приложение и хостить на разных доменах.
Автор: shirmanov