Faut-il passer sous MVC en .Net ? Si oui, quels sont les points forts de la dernière version MVC3 du framework ?
Principes de MVC
Le patron de conception (design pattern) Modèle, Vue, Contrôleur ou MVC a été inventé par Trygve Reenskaug en 1978. Présenté par son concepteur au sein d’un projet SmallTalk l’année suivante, il s’articule autour de trois composants :
- Modèle : c’est le composant qui contient les données et propose les méthodes pour accéder aux données et les mettre à jour. Typiquement, c’est lui qui contient les DTO (Data Transfert Object) d’un mapping relationnel dans le cas de l’utilisation d’une base relationnelle. Le composant modèle fournit également les services d’accès aux données et de traitement métier si une couche de services est présente dans l’application.
- Vue : ce composant contient l’interface utilisateur et a pour rôle de présenter les données renvoyées par le modèle à l’utilisateur sous une forme graphique. Il a aussi pour rôle de récupérer les actions de l’utilisateur pour les envoyer au contrôleur. Classiquement en architecture Web, la vue est faite en HTML.
- Contrôleur : il gère les événements et synchronise la vue et le modèle. Si une action utilisateur nécessite un changement des données, c’est le contrôleur qui va demander au modèle ce changement puis avertir la vue qu’elle doit se mettre à jour.
MVC appliqué à ASP .Net
Dans un premier temps, Microsoft a proposé de reprendre le fonctionnement des applications Windows pour le développement d’applications Web. Ceci facilite le portage d’application d’un monde vers l’autre, et permet également aux développeurs de concevoir les applications Web à l’aide de Webforms de la même façon que les applications Windows avec les Winforms. Ce choix pour le Web a pour conséquence l’utilisation d’une mécanique de page complexe avec un cycle de vie, l’utilisation de contrôleurs côté serveur, l’ajout de viewstates pour stoker l’état des contrôles etc. L’utilisation du framework ASP .Net MVC permet de développer des applications Web en .Net en respectant les principes MVC tout en s’affranchissant de l’utilisation des Webforms. ASP .Net MVC permet de faire le lien entre un contrôleur et une vue, n’a pas de viewstate, etc. Il favorise la conception pilotée par domaine, adaptée aux applications complexes, et les tests unitaires. Voyons le fonctionnement général du framework au travers d’un exemple de page nommée « User ».
Nous avons le code suivant dans la vue :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<asp:Content ID="TestContent" ContentPlaceHolderID="MainContent" runat="server> <% using (Html.BeginForm()) { %> <%: Html.ValidationSummary(true, "Erreur, veuillez corriger vos saisies") %> <div> <fieldset> <div class="editor-label> <%: Html.LabelFor(m => m.Nom) %> </div> <div class="editor-field> <%: Html.TextBoxFor(m => m.Nom)%> <%= Html.ValidationMessageFor(m => m.Nom)%> </div> <div class="editor-label> <%: Html.LabelFor(m => m.Prenom) %> </div> <div class="editor-field> <%: Html.TextBoxFor(m => m.Prenom)%> </div> <p> <input type="submit" value="Enregistrer" /> </p> <div class="editor-label> <%: Html.LabelFor(m => m.Valeur) %> </div> <div class="editor-label> <%: Html.DisplayFor(m => m.Valeur) %> </div> </fieldset> </div> <% } %> </asp:Content> |
La variable “m” correspond à l’objet modèle associé. Ici nous affichons dans des textbox HTML les attributs Nom et Prenom du modèle grâce au helper HTML « Html.TextBoxFor() » (ASP .Net MVC fournit des helpers pour faciliter l’affichage HTML de variables et modèles. Voir ci-dessous le paragraphe « Les nouveautés de ASP .Net MVC2 » pour plus d’explications). Le helper « Html.ValidationMessageFor() » sert à afficher les messages d’erreur relatifs aux règles qui s’appliquent sur l’attribut Nom. En dessous, l’attribut Valeur affiché correspond à une valeur retour permettant de voir que les données saisies par l’utilisateur sont bien passées au modèle. Nous avons enfin un formulaire avec un bouton de type submit. Ce bouton va déclencher une méthode dans le contrôleur que voici :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public ActionResult User() { return View(); } [HttpPost] public ActionResult User(User model, string returnUrl) { // Si le modèle ne passe pas la validation, on retourne la vue vide if (!ModelState.IsValid) return View(); // Ici on peut réaliser un enregistrement en base du modèle. return View(model); } |
La première méthode sans attribut correspond à l’affichage par défaut de la page. Elle retourne la vue sans modèle. La seconde méthode correspond à la requête « post » du formulaire et est donc déclenchée par l’action de l’appui sur le bouton submit. L’objet « User » en entrée est l’objet modèle renvoyé par la vue.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class User { [Required(ErrorMessage = "Le nom est requis")] [StringLength(30, ErrorMessage = "Le nom ne peut pas faire plus de 30 caractères")] [DisplayName("Nom")] public string Nom { get; set; } [DisplayName("Prénom")] public string Prenom { get; set; } [DisplayName("Utilisateur enregistré : ")] public string Valeur { get { return Nom + " " + Prenom; } } } |
L’objet « User » contient trois attributs Nom, Prenom et Valeur. Les deux premiers sont saisis par l’utilisateur, le troisième sert juste pour l’affichage. Les annotations sont utilisées ici pour indiquer à la vue quel label afficher pour l’attribut : c’est l’annotation « DisplayName ». Les annotations « Required » et « StringLenght » indiquent les règles de validation qui s’appliquent ainsi que le message d’erreur correspondant. Il est possible dans la méthode du contrôleur d’effectuer toutes les opérations que l’on souhaite sur le modèle comme l’enregistrer en base par exemple. La vue est ensuite retournée avec le modèle passé en paramètre. La vue va donc afficher les valeurs saisies par l’utilisateur ou les messages d’erreur appropriés en cas d’erreur de saisie :
Après le clic sur « Enregistrer » :
Un mot maintenant sur le routage dans ASP .Net MVC. C’est le système par lequel le framework va lier l’url à une action du contrôleur. Dans ASP .Net MVC, ce système est configuré dans le fichier Global.asax.cs comme suit par défaut :
1 2 3 4 5 6 7 8 9 |
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults ); } |
Par exemple, l’url « /User/Load/45 » va indiquer en premier le contrôleur à utiliser, en second l’action à déclencher et enfin le paramètre id. Cette url va exécuter le code « UserController.Load(45) ».
Les nouveautés de ASP .Net MVC2
La première version du framework fut suivie d’une seconde labélisée MVC2. Celle-ci est livrée au sein de Visual Studio 2010 et intègre de nombreuses nouveautés et améliorations dont nous faisons ici un bref récapitulatif :
Valeurs par défaut
Il est possible de définir des valeurs par défaut dans les attributs des méthodes des contrôleurs.
1 2 3 4 |
public ActionResult Test(string val = "valeur par défaut") { return View(); } |
Helpers
La première version du framework fournit des helpers HTML pour faciliter le développement des vues. En effet, ceux-ci génèrent une chaîne de caractère au format HTML. Par exemple « Html.TextBox(« valeur »,m.Valeur) » génère la balise HTML « input » permettant de saisir et de lier la donnée Valeur du modèle. Dans MVC2, les helpers intègrent une nouvelle syntaxe permettant de préciser le type de la donnée à lier. Ceci évite certaines erreurs qui n’étaient pas visibles à la compilation avec la précédente syntaxe. La nouvelle syntaxe devient « Html.TextBox(m => m.Valeur) » .
Encodage HTML
Dorénavant, les helpers HTML renvoient un objet MvcHtmlString (au lieu de String) qui n’a pas besoin d’être réencodé pour l’affichage HTML.
Support des Areas
Il s’agit d’un découpage du site afin de regrouper ensemble certains modèles, certaines vues et certains contrôleurs. Ceci est fait dans un souci de clarté car quand un site atteint un trop grand nombre de pages, l’explorateur de solution peut vite devenir illisible. Il est ainsi par exemple possible de regrouper les vues, contrôleurs et modèle relatifs à l’administration du site, ceux relatifs au métier ou à l’authentification, etc. Une Area peut se présenter sous la forme d’un répertoire ou d’un projet à part. Dans ce dernier cas, cela n’a aucun impact sur le code.
Support des annotations
Il est désormais possible de placer des annotations dans les modèles afin de préciser les règles de validation sur les données. Cette validation se fait automatiquement dans la vue via le ModelState. Par exemple, le code suivant indique que la propriété Name est obligatoire et ne doit pas dépasser 30 caractères :
1 2 3 4 5 6 7 8 9 10 |
public class TestAnnotation { [Required(ErrorMessage = "Le nom est requis")] [StringLength(30, ErrorMessage = "Le nom ne peut dépasser 30 caractères")] public string Name { get; set; } } |
Il est aussi possible d’utiliser des expressions régulières ou une plage de valeurs pour préciser les règles de validation ou encore créer ses propres annotations.
Validation côté client
Les annotations précédentes permettent un contrôle côté serveur. Pour que ces contrôles soient faits côté client en JavaScript avant que la requête HTML ne soit envoyée, il suffit d’ajouter la fonction « Html.EnableClientValidation() ; » dans la vue.
Templated Helpers
Les helpers « Html.DisplayForModel() » et « Html.TextBoxForModel() » permettent d’afficher facilement un formulaire de consultation, d’édition et de création pour le modèle. Il est aussi possible de développer des templates spécifiques pour les helpers.
Méta-données du modèle
MVC2 ajoute la possibilité de définir des métas-données sur les objets du modèle via l’utilisation d’annotations supplémentaires. Sur l’exemple ci-dessous, on indique que la propriété Id doit rester cachée dans la vue et que la propriété Name doit être labélisée « Nom » dans la vue.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class TestAnnotation { [Required(ErrorMessage = "Le nom est requis")] [StringLength(30, ErrorMessage = "Le nom ne peut dépasser 30 caractères")] [DisplayName("Nom")] public string Name { get; set; } [HiddenInput(DisplayValue = false)] public int Id { get; set; } } |
Actions asynchrones
Le développeur peut désormais au sein d’un contrôleur effectuer des traitements en mode asynchrone afin de paralléliser certains traitements côté serveur.
Templates personnalisés
MVC2 ajoute la possibilité de créer de nouveaux templates pour l’affichage des helpers suivant le type du modèle utilisé.
Les nouveautés de ASP .Net MVC3
La dernière version de ASP .Net MVC labélisée MVC3 et sortie en début d’année 2011 apporte son lot de nouveautés. En voici un bref aperçu :
Razor
C’est un nouveau moteur d’affichage pour les vues qui remplace le moteur ASPX standard. Visual Studio 2010 laisse le choix entre les deux moteurs lors de la création d’un nouveau projet. Ce nouveau moteur permet de réduire le code nécessaire à écrire dans les vues et facilite la compréhension. Il ne demande pas de nouveau langage à maîtriser, possède l’IntelliSense et permet de mettre en place des tests unitaires sans utiliser un contrôleur.
Validation
MVC étant compilé avec le Framework 4.0, toutes les annotations de validation sont dorénavant prises en charge côté client. Ceci permet de gérer facilement la localisation des champs dans les annotations. Ainsi, dans l’exemple ci-dessous, le label (utilisé par les helpers HTML) de l’attribut Name sera recherché dans la ressource ModelResources et sera donc localisé :
1 2 3 4 5 6 |
[Display(Name = "NOM", Description = "NOMDesc", ResourceType = typeof(ModelResources))] public string Name { get; set; } |
A cela s’ajoute l’apparition de deux nouvelles annotations de validation. La première permet de valider un champ en comparaison avec un autre. La seconde permet d’effectuer un contrôle asynchrone sur le champ côté serveur. Une autre nouveauté consiste à pouvoir désactiver le contrôle sur l’injection de balises HTML sur un champ précis au lieu de tout le modèle. Ceci permet par exemple d’autoriser l’utilisateur à saisir du code HTML dans seulement certains champs de formulaire et non dans tous. MVC3 offre maintenant la possibilité, via une interface IValidatableObject dont doit hériter l’objet modèle, d’ajouter des validations logiques une fois les validations liées aux annotations passées.
Global Action Filters
Dans ASP .Net MVC 1 et 2, il est possible de spécifier des filtres sur les contrôleurs. Ces filtres permettent de modifier la manière dont s’exécutent les actions. Il s’agit en fait de faire une classe qui hérite de la classe abstraite « ActionFilterAttribute » et qui va surcharger les quatre méthodes « OnActionExecuted, OnActionExecuting, OnResultExecuted, OnResultExecuting ». Ensuite, en plaçant l’attribut devant le contrôleur, les méthodes surchargées sont alors appelées à chaque appel de méthode du contrôleur. Dans ASP .Net MVC, les filtres sont un moyen élégant de gérer les logs ou encore la gestion des permissions. MVC3 offre la possibilité de définir des filtres généraux à tous les contrôleurs de l’application et même d’appliquer ces filtres que sous certaines conditions, comme par exemple seulement en mode débug.
Rendu HTML
MVC3 ajoute la possibilité d’utiliser des métas-données personnalisées. Les helpers d’affichage des messages d’erreur ont été améliorés pour non plus renvoyer le premier message mais le premier message ne provenant pas d’une exception. Les helpers Html.Label et Html.LabelFor incluent des surcharges permettant de remplacer le texte du label.
Nouveaux ActionResult
MVC3 ajoute les types de retour pour les contrôleurs HttpNotFoundResult et HttpStatusCodeResult. Ceci permet de retourner une vue d’erreur en cas d’absence de données ou de page interdite avec éventuellement un code retour. La classe RedirectResult prend un nouveau paramètre dans son constructeur pour indiquer une redirection permanente.
JavaScript et AJAX
Les helpers AJAX et Javascript ont été améliorés pour permettre l’utilisation du javascript discret et de jQuery. Le principe du javaScript discret consiste à placer les évènements javascript dans les fichiers javascript et non plus dans le code HTML. Cela est possible en utilisant le DOM pour placer des appels de fonctions javascript sur les évènements des éléments HTML.
La classe JsonValueProviderFactory
Avec MVC2, pour récupérer des données JSON d’une vue et les binder, il fallait implémenter un binder personnalisé. La nouvelle classe JsonValueProviderFactory récupère les valeurs des données JSON sous forme de paires clé-valeur et ces valeurs sont par conséquent disponibles pour le binder du modèle.
Autres améliorations
MVC3 intègre les dernières librairies jQuery et le plugin de validation jQuery. De plus, MVC3 offre des points d’extension via des interfaces permettant d’intégrer plus facilement un framework gérant les injections de dépendance. MVC3 offre aussi des améliorations au niveau du système de cache. Il est désormais possible de désactiver la session au niveau d’un contrôleur ou de la passer en lecture seule. Enfin, Razor permet d’ajouter un code exécuté avant le code de toutes les vues de l’application.
Avantages et inconvénients du framework ASP .Net MVC par rapport aux Webforms ASP
Avantages
- MVC simplifie la gestion d’une application en la divisant en un modèle, une vue et un contrôleur.
- MVC n’utilise pas le viewstate, ou les contrôles serveur. Cela rend le Framework MVC idéal pour les développeurs qui veulent un contrôle total sur le comportement de l’application.
- MVC utilise un pattern Contrôleur frontal (Front Controller), qui traite les requêtes de l’application Web par l’intermédiaire d’un contrôleur unique. Cela permet de concevoir une application qui prend en charge une infrastructure de routage riche.
- MVC offre un meilleur support pour le développement dirigé par les tests (TDD).
- MVC fonctionne bien pour des applications Web développées et soutenues par de grandes équipes de développeurs et de designers, qui ont besoin d’un degré élevé de contrôle sur le comportement des applications.
- MVC offre une structure claire du projet avec la séparation entre les vues, modèles et contrôleurs ainsi que l’utilisation des areas.
- A l’aide du framework MVC, il est facile d’ouvrir l’application à d’autres supports (PC, smart phone, etc.) en proposant un jeu de vues par type d’interface utilisateur, les modèles et contrôleurs n’étant pas impactés.
Inconvénients
- Contrairement aux Webforms, l’état des données entre deux requêtes HTTP n’est pas préservé avec MVC.
- MVC ne permet pas l’utilisation de composants Web comme les GridView.
- MVC nécessite généralement plus de code que pour du développement Webforms même si ce défaut semble s’atténuer au fil des versions.
Conclusion
Si historiquement ASP .Net a été créé dans le but de facilement développer des applications Web, le besoin de support d’une architecture plus structurée s’est fait ressentir avec les années. Afin de se conformer aux standards existants dans les mondes du J2EE et PHP et pour faire face à la montée des frameworks MVC, Microsoft a su adapter son environnement afin d’offrir le support du modèle MVC. D’une première mouture assez légère, Microsoft a su faire évoluer son framework pour aboutir à MVC3, ce dernier offrant une alternative mature et crédible aux Webforms ou encore aux autres frameworks MVC des univers J2EE et PHP.