Как изменить представления ASP.NET MVC в зависимости от типа устройства?

StackOverflow https://stackoverflow.com/questions/1387354

Вопрос

Я работаю над чтением ASP.NET MVC, и у меня на работе есть веб-приложение, которое я буду переносить с WebForms на MVC.Один из запросов на функцию, который я ожидаю получить в процессе, — это возвращение упрощенного представления, если пользователь заходит с мобильного устройства.

Я не совсем понимаю, где лучше всего реализовать такую ​​логику.Я уверен, что есть лучший способ, чем добавление if/else для Browser.IsMobileDevice в каждом действии, возвращающем представление.Какие варианты у меня есть для этого?

Это было полезно?

Решение

Обновлять:В этом решении есть небольшая ошибка.Фреймворк MVC вызовет FindView/FindPartialView дважды:однажды с useCache=true, и если это не возвращает результат, один раз с useCache=false.Поскольку для всех типов представлений существует только один кэш, мобильные пользователи могут в конечном итоге видеть представления с настольных компьютеров, если браузер для настольных компьютеров появился первым.

Для тех, кто заинтересован в использовании пользовательских механизмов просмотра для решения этой проблемы, Скотт Хансельман обновил свое решение здесь:

http://www.hanselman.com/blog/ABetterASPNETMVCMobileDeviceCapabilitiesViewEngine.aspx

(Извиняюсь за перехват ответа, я просто не хочу, чтобы кому-то еще пришлось пройти через это!)

Под редакцией roufamatic (17 ноября 2010 г.)


Первое, что вы хотите сделать, это представить Файл браузера мобильного устройства в ваш проект.Используя этот файл, вы можете настроить таргетинг на любое устройство, которое хотите поддерживать, не зная особенностей того, что эти устройства отправляют в своих заголовках.Этот файл уже сделал всю работу за вас.Затем вы используете свойство Request.Browser, чтобы выбрать, какое представление вы хотите вернуть.

Затем придумайте стратегию организации представлений в папке «Представления».Я предпочитаю оставить настольную версию в корне, а затем создать папку Mobile.Например, папка домашнего просмотра будет выглядеть так:

  • Дом
    • мобильный
      • айфон
        • Индекс.aspx
      • Ежевика
        • Индекс.aspx
    • Индекс.aspx

Я не согласен с @Mehrdad по поводу использования пользовательского механизма просмотра.Механизм представления служит более чем одной цели, и одна из этих целей — поиск представлений для контроллера.Это можно сделать, переопределив метод FindView.С помощью этого метода вы можете проверить, где найти представление.После того, как вы узнаете, какое устройство использует ваш сайт, вы можете использовать разработанную вами стратегию организации представлений, чтобы вернуть представление для этого устройства.

public class CustomViewEngine : WebFormViewEngine
{
    public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
    {
        // Logic for finding views in your project using your strategy for organizing your views under the Views folder.
        ViewEngineResult result = null;
        var request = controllerContext.HttpContext.Request;

        // iPhone Detection
        if (request.UserAgent.IndexOf("iPhone",
   StringComparison.OrdinalIgnoreCase) > 0)
        {
            result = base.FindView(controllerContext, "Mobile/iPhone/" + viewName, masterName, useCache);
        }

        // Blackberry Detection
        if (request.UserAgent.IndexOf("BlackBerry",
   StringComparison.OrdinalIgnoreCase) > 0)
        {
            result = base.FindView(controllerContext, "Mobile/BlackBerry/" + viewName, masterName, useCache);
        }

        // Default Mobile
        if (request.Browser.IsMobileDevice)
        {
            result = base.FindView(controllerContext, "Mobile/" + viewName, masterName, useCache);
        }

        // Desktop
        if (result == null || result.View == null)
        {
            result = base.FindView(controllerContext, viewName, masterName, useCache);
        }

        return result;
    }
}

Приведенный выше код позволяет вам настроить представление на основе вашей стратегии.Альтернативным вариантом является представление на рабочем столе, если для устройства не найдено представление или если не существует представления для мобильных устройств по умолчанию.

Если вы решите поместить логику в свой контроллер вместо создания механизма представления.Лучшим подходом было бы создать собственный Атрибут ActionFilter которым вы можете украсить свой контроллер.Затем переопределите OnActionExecuted метод определения того, какое устройство просматривает ваш сайт.Вы можете проверить это Сообщение блога о том, как это сделать.В посте также есть несколько хороших ссылок на несколько видеороликов Mix на эту тему.

Другие советы

В шаблоне Модель-Представление-Контроллер контроллер выбирает представление, поэтому не так уж и плохо добавить if оператор и вернуть соответствующее представление.Вы можете инкапсулировать if оператор в методе и вызовите его:

return AdaptedView(Browser.IsMobileDevice, "MyView.aspx", model);

Альтернативно вы можете создать механизм представления, который динамически выполняет представление в зависимости от того, мобильно оно или нет.Я не сторонник такого подхода, поскольку считаю, что ответственность должен нести контролер.Например, если вы просматриваете сайт на iPhone, возможно, вам захочется вместо этого увидеть полную версию для настольного компьютера.В первом подходе вы передаете соответствующий логический флаг, но во втором все становится сложнее.

Я думаю, что правильным местом для подключения этой функциональности является пользовательский ViewEngine.Но вы должны знать, как IViewEngine.FindView метод вызывается ViewEngineCollection (подробнее об этом здесь).

Обновлено решение предложенный Скоттом Хансельманом, работает неправильно.Вы можете найти мой пример реализации этого подхода. здесь.Проверьте файл readme, в котором описано, как можно повторить неправильное поведение.

Я предлагаю другой подход, который проверяет, не было ли представление найдено исходным ViewEngine, и если useCache параметр true, он проверяет, существует ли представление в исходном ViewEngine с параметром useCache=false.

Слишком сложно разместить здесь весь код, но вы можете найти предложенный подход, реализованный на моей игровой площадке с открытым исходным кодом. здесь.Проверять MobileViewEngine классовые и модульные тесты.

Некоторые возможности MobileViewEngine:

  • Корректно работает с кэшированием представлений и использует исходный кеш механизма просмотра.
  • Поддерживает оба:Имена видов снимков и относительные пути просмотра (~/Views/Index), используемые шаблоном MvcContrib T4.
  • Разрешает представление «Индекс» следующим образом:
    • Mobile/Platform/Index – если представление существует и платформа мобильного устройства (IPhone, Android и т. д.) указана в списке поддерживаемых.
    • Mobile/Index - просмотр для всех остальных мобильных устройств.Если представление не существует, вы можете при необходимости вернуться к версии представления для настольного компьютера.
    • Index – для настольной версии.
  • Вы можете настроить иерархию мобильных представлений (например, Mobile/ Platform/Manufacturer) или настройте разрешение пути просмотра на мобильных устройствах, добавив или изменив правила для устройств (см. MobileDeviceRule и PlatformSpecificRule).

Надеюсь, это поможет

Это версия, которая действительно работает как с T4MVC, так и в режиме выпуска (где включено кэширование представлений).Он также заботится о пользовательских элементах управления и абсолютных/относительных URL-адресах.Это требует Файл браузера мобильного устройства.

public class MobileCapableWebFormViewEngine : WebFormViewEngine
{

    protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
    {
        if (viewPath.EndsWith(".ascx"))
            masterPath = "";
        return base.CreateView(controllerContext, viewPath, masterPath);
    }
    public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
    {
        useCache = false;
        ViewEngineResult result = null;
        var request = controllerContext.HttpContext.Request;

        if (request.Browser.IsMobileDevice || request["mobile"] != null || request.Url.Host.StartsWith("m."))
        {
            var mobileViewName = GetMobileViewName(viewName);

            result = base.FindView(controllerContext, mobileViewName, masterName, useCache);
            if (result == null || result.View == null)
            {
                result = base.FindView(controllerContext, viewName, "Mobile", useCache);
            }
        }

        if (result == null || result.View == null)
        {
            result = base.FindView(controllerContext, viewName, masterName, useCache);
        }

        return result;
    }

    private static string GetMobileViewName(string partialViewName)
    {
        var i = partialViewName.LastIndexOf('/');
        return i > 0
                   ? partialViewName.Remove(i) + "/Mobile" + partialViewName.Substring(i)
                   : "Mobile/" + partialViewName;
    }
}

Ваша основная логика должна быть одинаковой в контроллерах, и изменится только то представление, которое вам нужно, поэтому контроллер находится там, где вам нужен оператор if/else, чтобы обеспечить правильное представление для каждого действия контроллера, как вы сказали.

Альтернативой было бы поместить логику контроллера в отдельную dll, а затем использовать разные контроллеры/пути для мобильной версии.Если обычный контроллер получает запрос от мобильного устройства, вы можете перенаправить его в свою мобильную зону, которая содержит все ваши мобильные контроллеры, использующие общую логику контроллера.Это решение также позволит вам выполнять «настройки», специфичные для мобильных контроллеров, и не влиять на ваши обычные контроллеры.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top