2009-09-07 11 views
27

Sto lavorando su alcune letture ASP.NET MVC e ho un'app Web al lavoro che migrerò da WebForms a MVC. Una delle richieste di funzionalità che mi aspetto di ottenere nel processo è avere una vista semplificata restituita se l'utente proviene da un dispositivo mobile.Come modifico le viste ASP.NET MVC in base al tipo di dispositivo?

Non riesco a capire dove sia il posto migliore per implementare quel tipo di logica. Sono sicuro che c'è un modo migliore di aggiungere un if/else per Browser.IsMobileDevice in ogni azione che restituisce una vista. Che tipo di opzioni dovrei fare?

risposta

21

Aggiornamento: questa soluzione ha un bug sottile. Il framework MVC chiamerà in FindView/FindPartialView due volte: una volta con useCache=true e se ciò non restituisce un risultato, una volta con useCache=false. Dal momento che esiste una sola cache per tutti i tipi di visualizzazioni, gli utenti mobili potrebbero vedere le visualizzazioni desktop se prima arrivava un browser desktop.

Per chi fosse interessato ad utilizzare i motori di visualizzazione personalizzata per risolvere questo problema, Scott Hanselman ha aggiornato la sua soluzione qui:

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

(Ci scusiamo per la risposta dirottamento, io non voglio nessun altro a devono passare attraverso questo!)

a cura di roufamatic (2010-11-17)


La prima cosa che si vuole fare è introdurre lo Mobile Device Browser File nel proprio progetto. Usando questo file puoi scegliere come target qualsiasi dispositivo che vuoi supportare senza dover conoscere le specifiche di ciò che i dispositivi inviano nelle loro intestazioni. Questo file ha già fatto il lavoro per te. Quindi si utilizza la proprietà Request.Browser per personalizzare la vista che si desidera restituire.

Successivamente, trovare una strategia su come si desidera organizzare le visualizzazioni nella cartella Visualizzazioni.Preferisco lasciare la versione desktop alla radice e quindi avere una cartella Mobile. Ad esempio, la cartella di vista iniziale sarebbe simile a questa:

  • casa
    • mobile
      • iPhone
        • Index.aspx
      • BlackBerry
        • Index.aspx
    • Index.aspx

Non sono d'accordo con @Mehrdad sull'utilizzo di un motore di visualizzazione personalizzata. Il motore di visualizzazione ha più di uno scopo e uno di questi è trovare le viste per il controller. Puoi farlo sostituendo il metodo FindView. Con questo metodo, puoi verificare dove trovare la vista. Dopo aver saputo quale dispositivo sta utilizzando il tuo sito, puoi utilizzare la strategia che hai scelto per organizzare le tue visualizzazioni per restituire la visualizzazione per quel 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; 
    } 
} 

Il codice sopra consente di impostare la vista in base alla propria strategia. Il fall back è la vista desktop, se non è stata trovata alcuna vista per il dispositivo o se non c'è una vista mobile predefinita.

Se si decide di inserire la logica nel controller anziché creare un motore di visualizzazione. L'approccio migliore sarebbe quello di creare una personalizzata ActionFilterAttribute con cui decorare il controller. Quindi, ignora il metodo OnActionExecuted per determinare quale dispositivo sta visualizzando il tuo sito. Puoi controllare questo blog post su come. Il post contiene anche alcuni link utili ad alcuni video Mix su questo argomento.

+0

non funzionerà con T4MVC, si assume il viewname è solo un nome e non un percorso. inoltre non funzionerà in modalità di rilascio a causa della memorizzazione nella cache. –

+0

Non l'ho provato da quando è stato rilasciato T4MVC. Questo era prima che fosse disponibile. Quando avrò tempo, aggiornerò la risposta per riflettere i miei risultati durante i test contro T4MVC. –

+0

Carl, cosa intendi con il caching? Questo approccio è identico a ciò che Scott Hanselman descrive qui: http://www.hanselman.com/blog/MixMobileWebSitesWithASPNETMVCAndTheMobileBrowserDefinitionFile.aspx e lui non lo menziona. – roufamatic

2

Nel modello Model-View-Controller, è il controller che sceglie la vista, quindi non è così male aggiungere un'istruzione if e restituire una vista appropriata. È possibile incapsulare la dichiarazione if in un metodo e chiamare:

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

In alternativa, è possibile creare un motore di visualizzazione che esegue dinamicamente una vista basata su che si tratti di mobili o no. Non sono un fan di questo approccio dal momento che ritengo che il controller debba essere responsabile. Ad esempio, se stai navigando su iPhone, potresti invece vedere la versione desktop completa. Nell'approccio precedente, si passerebbe la bandiera booleana appropriata, ma nel secondo caso le cose si complicheranno.

0

La logica di base deve essere la stessa nei controller e solo la vista che è necessario cambierà in modo che il controller sia dove è necessario che l'istruzione if/else offra la visualizzazione corretta per ogni azione del controller.

Un'alternativa sarebbe quella di avvolgere la logica del controller in una DLL separata e quindi disporre di diversi controller/percorsi per la versione mobile. Se un controller regolare riceve una richiesta da un dispositivo mobile, è possibile reindirizzarlo alla propria area mobile che contiene tutti i controller mobili che utilizzano la logica del controller condiviso. Questa soluzione consentirebbe anche di fare "tweek" specifici per i controller mobili e non avere impatto sui normali controller.

1

Questa è una versione che funziona effettivamente, sia con T4MVC sia in modalità di rilascio (in cui è abilitata la memorizzazione nella cache delle viste). Si prende cura degli usercontrols e degli url assoluti/relativi. Richiede il 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

Grazie per il follow up, ma non vedo ancora perché non ti fidi della cache. AFAICT che consente la memorizzazione nella cache deve accelerare le chiamate in base.FindView(). Quelle chiamate stanno accadendo dopo che è stata scelta la vista corretta. Il controllo con Reflector conferma. Posso capire la disattivazione della memorizzazione nella cache se si prevede che tali visualizzazioni cambieranno, ma non è ciò che sta accadendo qui. Le opinioni sono le stesse, stiamo solo aggiungendo la logica per come sceglierle. Continuerò con il cache. – roufamatic

2

Penso che il posto giusto per collegare questa funzionalità sia personalizzato ViewEngine. Ma si dovrebbe essere consapevoli di come il metodo IViewEngine.FindView viene chiamato dallo ViewEngineCollection (per ulteriori informazioni su questo here).

Aggiornamento solution suggerito da Scott Hanselman non funziona correttamente. Puoi trovare la mia implementazione esemplificativa di questo approccio here. Controlla il file readme che descrive come puoi ripetere un comportamento scorretto.

suggerisco un altro approccio che controlla se la vista non è stato trovato da ViewEngine originale e se useCache parametro è true, controlla se esiste in vista ViewEngine originale con il parametro useCache=false.

È troppo complesso inserire tutto il codice qui, ma è possibile trovare l'approccio suggerito implementato nel mio parco giochi open-source here. Verifica i test di classe e unità MobileViewEngine.

Alcune caratteristiche MobileViewEngine:

  • funziona correttamente con vista caching e utilizza cache di vista del motore originale.
  • Supporta entrambi: i nomi di visualizzazione shot e i relativi percorsi di visualizzazione (~/Views/Index) utilizzati dal modello T4 di MvcContrib.
  • Risolve "Indice" Vedi come segue:
    • Mobile/Platform/Index - se vista esiste e una piattaforma per dispositivi mobili (iPhone, Android, ecc) viene arruolato nella lista supportati.
    • Mobile/Index - visualizzazione per tutti gli altri dispositivi mobili. Se la vista non esiste, puoi facoltativamente eseguire il fallback alla versione desktop view.
    • Index - per versione desktop view.
  • È possibile personalizzare la visualizzazione della gerarchia cellulare (ad esempio Mobile/ Platform/Manufacturer) o personalizzare risoluzione percorso vista mobile aggiungendo/cambiando le regole del dispositivo (vedi MobileDeviceRule e PlatformSpecificRule).

Speranza, questo aiuterà

+0

+1 ha aiutato, grazie –

Problemi correlati