Подтвердить что ты не робот

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

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

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

4b9b3361

Ответ 1

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

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

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

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

Отредактировано roufamatic (2010-11-17)


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

Далее, придумайте стратегию о том, как вы хотите упорядочить свои представления в папке "Представления". Я предпочитаю оставить версию рабочего стола в корневом каталоге, а затем иметь папку для мобильных устройств. Например, папка Home view будет выглядеть так:

  • Главная
    • Мобильный
      • iPhone
        • Index.aspx
      • BlackBerry
        • Index.aspx
    • index.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;
    }
}

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

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

Ответ 2

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

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

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

Ответ 3

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

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

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

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

Некоторые функции MobileViewEngine:

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

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

Ответ 4

Это версия, которая действительно работает, как с T4MVC, так и в режиме выпуска (где включено кэширование представлений). Он также заботится о usercontrols и абсолютных/относительных 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;
    }
}

Ответ 5

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

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