mardi 10 novembre 2009

ASP.NET MVC : Changer de ViewEngine (1/2)

Nous avons vu dans un précédent post comment changer de ControllerFactory, maintenant nous allons voir comment changer de ViewEngine.

Mon problème, comme la dernière fois, est que je n’aime pas la convention par défaut de MVC : si je ne mets pas mes vues dans le dossier “Views”, il ne les trouve pas. Je vais donc créer mon propre ViewEngine ; ou plutot je vais étendre celui utilisé par défaut pour redéfinir les dossiers où chercher.

Avant de faire ça, nous allons d’abord regarder comment fonctionne le WebFormViewEngine : le code source est disponible, ne nous privons pas d’aller y jeter un oeil !
La classe hérite de VirtualPathProviderViewEngine, classe abstraite qui permet d’aller chercher les fichiers dans le site à partir de chemins virtuels asp.net (vous savez, ceux qui commencent par ~). Regardons son constructeur :

    public WebFormViewEngine() {
        MasterLocationFormats =
new[] {
           
"~/Views/{1}/{0}.master",
           
"~/Views/Shared/{0}.master"
        };

        ViewLocationFormats =
new[] {
           
"~/Views/{1}/{0}.aspx",
           
"~/Views/{1}/{0}.ascx",
           
"~/Views/Shared/{0}.aspx",
           
"~/Views/Shared/{0}.ascx"
        };

        PartialViewLocationFormats = ViewLocationFormats;
    }

Ici on initialise 3 tableaux de String, qui contiennent les emplacements où chercher nos vues, pour les master pages, les vues et les vues partielles. Ainsi, si je veux redéfinir l’emplacement de mes vues, je pourrai me contenter d’hériter de WebFormViewEngine et de remplacer ces valeurs par les miennes :

public class MyCustomViewEngine : WebFormViewEngine
{
   
public MyCustomViewEngine()
    {
        MasterLocationFormats =
new[] {
           
"~/MesMasterPages/{1}/{0}.master",
           
"~/MesMasterPages/{0}.master"
        };

        ViewLocationFormats =
new[] {
           
"~/MesVues/{1}/{0}.aspx",
           
"~/MesVues/{0}.aspx"
        };

        PartialViewLocationFormats =
new[] {
           
"~/MesVues/{1}/{0}.ascx",
           
"~/MesVues/{0}.ascx"
        };
    }
}

On va maintenant aller un peu plus loin, en créant un ViewEngine qui va renvoyer des vues différentes en fonction du contexte. Un exemple pratique : on veut créer des vues spécifiques pour les appareils mobiles.

Par exemple, si je veux définir un site “normal” à l’adresse http://www.monsite.com, et un site mobile à l’adresse http://m.monsite.com, je vais router les dns de mes deux sous-domaines vers le même site, et mon ViewEngine fera le tri :

public class MobileViewSelector : WebFormViewEngine
{
   
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
    {
       
// on récupère le domaine utilisé pour venir sur le site
       
String domain = controllerContext.RequestContext.HttpContext.Request.Url.Authority.ToLower();

       
// si on est sur un domaine pour mobile, on va chercher la vue dans un sous-dossiers "mobile"
       
if (domain.StartsWith("m") || domain.StartsWith("iphone"))
            viewName =
"Mobile/" + viewName;

       
// on appelle la requête de base
       
return base.FindView(controllerContext, viewName, masterName, useCache);
    }
}

Dans cet exemple, si on se connecte au site en utilisant le sous domaine “m” (ou même “iphone”, parce que c’est à la mode, même si le résultat est exactement le même), la vue sera recherchée dans un sous-dossier “Mobile” du dossier des vues, sinon on récupère la vue normale. Bien sûr, l’utilisation du sous domaine n’est qu’un exemple, vous pouvez router vos vues en fonction de variables de sessions, de paramètres passés dans la requête ou tout ce que vous désirez.

Il suffit ensuite de créer toutes nos vues en double : une vue normale, et une vue mobile, et ça fonctionnera. C’est là un des points forts de MVC, le contrôleur n’a pas besoin d’être modifié : il fait uniquement son boulot de contrôleur et envoie des informations à la vue. Ensuite que la vue soit faite pour un PC ou un mobile ne le concerne plus.

Dernière petite chose pour que ça fonctionne, il ne faut pas oublier d’enregistrer notre ViewEngine dans le global.asax :

protected void Application_Start()
{
    RegisterRoutes(
RouteTable.Routes);

   
// On supprime le ViewEngine par défaut, on n'en a plus besoin
   
ViewEngines.Engines.Clear();

   
// On enregistre notre ViewEngine
   
ViewEngines.Engines.Add(new MobileViewSelector());
}

Nous verrons dans un prochain post comment modifier le ViewEngine plus en profondeur. Nous n’avons ici modifié que l’appel des fichiers aspx, mais il est possible de modifier totalement le système de rendu, si par exemple vous ne souhaitez pas utiliser de webforms.

Cross-posté vers Tech Head Brothers