2009-09-07 6 views
27

Estoy trabajando en mi lectura de ASP.NET MVC y tengo una aplicación web en el trabajo que voy a migrar de WebForms a MVC. Una de las solicitudes de funciones que espero obtener en el proceso es que se devuelva una vista simplificada si el usuario proviene de un dispositivo móvil.¿Cómo cambiaría las vistas ASP.NET MVC según el tipo de dispositivo?

No puedo ver dónde está el mejor lugar para implementar ese tipo de lógica. Estoy seguro de que hay una manera mejor que agregar un if/else para Browser.IsMobileDevice en cada acción que devuelve una vista. ¿Qué tipo de opciones tendría que hacer esto?

Respuesta

21

Actualización: Esta solución tiene un error sutil. El framework MVC llamará al FindView/FindPartialView dos veces: una vez con useCache=true, y si eso no arroja un resultado, una vez con useCache=false. Como solo hay un caché para todos los tipos de vistas, los usuarios de dispositivos móviles pueden terminar viendo vistas de escritorio si el navegador de escritorio fue el primero en llegar.

Para aquellos interesados ​​en el uso de motores de vista personalizada para resolver este problema, Scott Hanselman ha actualizado su solución aquí:

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

(Disculpas por el secuestro respuesta, simplemente no quieren que nadie más lo tienen que pasar por esto!)

Editado por roufamatic (2010-11-17)


Lo primero que debe hacer es introducir el Mobile Device Browser File en su proyecto. Al usar este archivo, puede orientarse a cualquier dispositivo que desee admitir sin tener que conocer los detalles de lo que esos dispositivos envían en sus encabezados. Este archivo ya ha hecho el trabajo por usted. A continuación, utiliza la propiedad Request.Browser para personalizar la vista que desea devolver.

A continuación, presente una estrategia sobre cómo desea organizar sus vistas en la carpeta Vistas.Prefiero dejar la versión de escritorio en la raíz y luego tener una carpeta móvil. Por ejemplo, la carpeta de vista inicial se vería así:

  • Inicio
    • móvil
      • iPhone
        • Index.aspx
      • BlackBerry
        • Index.aspx
    • Index.aspx

puedo estar de acuerdo con @Mehrdad sobre el uso de un motor de vista personalizada. El motor de vista cumple más de un propósito y uno de esos propósitos es encontrar vistas para el controlador. Para ello, anule el método FindView. En este método, puede hacer sus comprobaciones sobre dónde encontrar la vista. Una vez que sepa qué dispositivo está usando su sitio, puede usar la estrategia que se le ocurrió para organizar sus vistas y devolver la vista para ese dispositivo.

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; 
    } 
} 

El código anterior le permite configurar la vista en función de su estrategia. El retroceso es la vista de escritorio, si no se encontró vista para el dispositivo o si no hay una vista móvil predeterminada.

Si decide poner la lógica en su controlador en lugar de crear un motor de vista. El mejor enfoque sería crear un ActionFilterAttribute personalizado con el que pueda decorar su controlador. Luego, anule el método OnActionExecuted para determinar qué dispositivo está viendo su sitio. Puede consultar esto blog post sobre cómo hacerlo. La publicación también tiene algunos buenos enlaces a algunos videos Mix sobre este mismo tema.

+0

no funcionará con T4MVC, usted asume el nombredevista es sólo un nombre y no un camino. tampoco funcionará en el modo de lanzamiento debido al almacenamiento en caché. –

+0

No lo he probado desde que se lanzó T4MVC. Esto fue antes de que estuviera disponible. Cuando tenga tiempo, actualizaré la respuesta para reflejar mis resultados cuando realice pruebas contra T4MVC. –

+0

Carl, ¿qué quiere decir con el almacenamiento en caché? Este enfoque es idéntico al que describe Scott Hanselman aquí: http://www.hanselman.com/blog/MixMobileWebSitesWithASPNETMVCAndTheMobileBrowserDefinitionFile.aspx y él no lo menciona. – roufamatic

2

En el patrón Modelo-Vista-Controlador, es el controlador que elige ver, por lo tanto, no es tan malo agregar una declaración if y devolver una vista adecuada. Puede encapsular la declaración if en un método y llamarlo:

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

Como alternativa, puede crear un motor de vista que se ejecuta dinámicamente una vista en función de si es o no móvil. No soy fanático de este enfoque ya que creo que el controlador debería estar a cargo. Por ejemplo, si está navegando en un iPhone, es posible que desee ver la versión de escritorio completa. En el primer enfoque, pasaría el indicador booleano apropiado, pero en este último, las cosas se vuelven más complicadas.

0

Su lógica central debe ser la misma en los controladores y solo cambiará la vista que necesita para que el controlador esté donde usted necesita la instrucción if/else para mostrar la vista correcta para cada acción del controlador como usted indicó.

Una alternativa sería envolver la lógica del controlador en un dll independiente y luego tener diferentes controladores/rutas para la versión móvil. Si un controlador regular recibe una solicitud de un dispositivo móvil, puede redirigirlos a su área móvil que contiene todos sus controladores móviles que usan la lógica de controlador compartido. Esta solución también le permitirá hacer 'tweeks' que son específicos de los controladores móviles y no hacer que afecten a sus controladores habituales.

1

Esta es una versión que realmente funciona, tanto con T4MVC como en modo de lanzamiento (donde está habilitado el almacenamiento en caché de vistas). También se ocupa de los controles de usuario y de las URL absolutas/relativas. Requiere el Mobile Device Browser File.

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; 
    } 
} 
+0

Gracias por hacer un seguimiento, pero todavía no veo por qué no confía en el caché. AFAICT permitir el almacenamiento en caché debe acelerar las llamadas en base.FindView(). Esas llamadas están sucediendo después de que se haya elegido la vista correcta. La comprobación con Reflector confirma. Puedo entender la desactivación del almacenamiento en caché si espera que esas vistas cambien, pero eso no es lo que está sucediendo aquí. Las vistas son las mismas, solo estamos agregando lógica para elegirlas. Me quedaré con el caché. – roufamatic

2

Creo que el lugar correcto para conectar esta funcionalidad es ViewEngine personalizado. Pero debe saber cómo el IViewEngine.FindView método es llamado por el ViewEngineCollection (para más información sobre este here).

Actualizado solution sugerido por Scott Hanselman no funciona correctamente. Puede encontrar la implementación de ejemplo de este enfoque here. Verifique el archivo léame que describe cómo puede repetir el comportamiento incorrecto.

I sugieren otro enfoque que comprueba si una vista no se encontró por ViewEngine original y si useCache parámetro es true, comprueba si existe en vista ViewEngine original con parámetro useCache=false.

Es demasiado complejo para poner todo el código aquí, pero puede encontrar el enfoque sugerido implementado en mi patio de juegos de código abierto here. Marque MobileViewEngine clase y pruebas unitarias.

Algunas características: MobileViewEngine

  • funciona correctamente con vista al almacenamiento en caché y utiliza caché original de vista motor.
  • Admite ambos: nombres de vista de toma y rutas de vista relativas (~/Vistas/Índice) utilizadas por la plantilla MvcContrib T4.
  • Resuelve "Índice" vista de la siguiente manera:
    • Mobile/Platform/Index - si existe vista y una plataforma de dispositivos móviles (iPhone, Android, etc.) está dado de alta en la lista de admitidos.
    • Mobile/Index - vista para todos los demás dispositivos móviles. Si la vista no existe, puede optar por recurrir a la versión de vista de escritorio.
    • Index - para la versión de vista de escritorio.
  • Puede personalizar jerarquía de vistas móvil (por ejemplo Mobile/ Platform/Manufacturer) o personalizar vista móvil resolución ruta mediante la adición de reglas de dispositivos/cambiantes (ver MobileDeviceRule y PlatformSpecificRule).

Esperanza, esto ayudará

+0

+1 fue útil, gracias –

Cuestiones relacionadas