Au cœur des différentes architectures Web (WebForms, Apache Struts, XSL/XML, HTML ...)
Cet article s’intéresse aux différentes manières de traiter les problématiques
de présentation. La première partie introduit les diverses facettes de cette
couche logicielle ; les deux suivantes présentent les solutions proposées
respectivement par la plateforme J2EE (à travers les frameworks de
présentation du type Struts) et la plateforme .NET (à travers le
framework des WebForms). Enfin, toujours dans un souci de comparaison,
nous aborderons une architecture de présentation basée entièrement sur XML et
XSL et nous tenterons de peser avantages et inconvénients de chaque
solution.
Le thème abordé ici est bien entendu très vaste, et nous ne saurions prétendre à l’exhaustivité. Aussi, après avoir planté le décor de la couche de présentation, nous nous intéresserons particulièrement à la manière dont les erreurs de saisie sont traitées dans les différents frameworks (et nous aborderons brièvement les sujets connexes, qui sont énoncés dans la première section de cet article).
1.
Restitution d’informations, présentation de services,
multi-canal
La principale responsabilité de la couche de présentation est bien entendu de permettre à l’utilisateur de visualiser informations (textes, graphiques, multimedia) et services dans l’équipement dont il dispose.
Cette activité de
« rendu » soulève des questions variées :
· L’ergonomie : doit-on utiliser du texte, des couleurs, des métaphores graphiques ou sonores
· Le multi-canal : l’utilisateur souhaite consulter les mêmes informations ou accéder aux mêmes services à travers le Web, le Wap, le téléphone vocal traditionnel, un assistant personnel (PDA)…
· L’internationalisation (i18n) : le service ou les informations doivent s’adapter aux habitudes linguistiques et culturelles de l’usager. Or, cela tombe bien, la plupart des outils et protocoles permettent la configuration et l’exploitation de la « langue préférée » et du pays de l’utilisateur. Cette information est capitale pour que le service traduise ses libellés dans la bonne langue, qu’il adapte le sens de lecture (français, arabe, chinois), ainsi que les métaphores (couleurs, icônes, sons…)
· La personnalisation : l’idéal, pour répondre au mieux aux utilisateurs, est de leur donner la possibilité de raffiner eux-mêmes les options de rendu (voire de contenu et de navigation, cf ci-après). Par exemple, la plupart des moteurs de recherche permettent de redéfinir le nombre de réponses attendues par l’utilisateur à chacune de ses requêtes. Cela peut également aller jusqu’à la modification de la charte graphique (ou du « thème », du « skin ») de notre système.
2.
Navigation à travers les différentes fonctionnalités
Cette responsabilité de la couche de présentation est très liée aux aspects précédents. La question est ici de savoir quel cheminement, quel synoptique de navigation dans les informations et services nous proposons aux utilisateurs.
Et bien entendu, comment faire en sorte de bien isoler cet aspect de celui du rendu ! En effet, comme on peut souhaiter modifier une charte graphique sans vouloir toucher à l’enchaînement des pages, on pourrait également désirer modifier la navigation dans un site indépendamment de la présentation (typiquement, certaines options de navigation ne sont accessibles qu’à certains utilisateurs disposant des bonnes accréditations. Exemple : les services protégés, les pages d’administration).
Il faut pouvoir décrire la navigation indépendamment de la présentation, et pouvoir, dans l’idéal, y inclure un minimum d’intelligence (accréditations, parcours « orienté » dans un site marchand, parcours « découverte » pour un type ciblé d’utilisateur…)
3.
Aide à la correction de saisie pour l’utilisateur
« Ce service refuse de fonctionner, et je ne parviens pas à savoir pourquoi ! »
« Cela fait quatre fois que l’on me demande de saisir les mêmes informations, pour la simple raison que j’ai fait une erreur de frappe dans ma date de naissance ou dans l’orthographe de mon lieu de naissance ! C’est un peu fort ! »
Ces utilisateurs vont forcément rechigner à utiliser notre service, ou aller voir ailleurs ! Pour éviter une telle situation, facilitons-leur la tâche : si, dans un questionnaire (formulaire) quelconque, ils saisissent dix informations et se trompent sur une ou deux d’entre elles, il serait de bon ton de :
· Mémoriser les entrées correctes,
· Inviter l’utilisateur à ne modifier que celles qui sont erronées,
· Indiquer pour cela quelle information ne convient pas, et pour quelle raison,
De cette manière, pas de fatalité ni de défaitisme dans l’esprit de nos usagers.
4.
Interactivité, réactivité
L’informatique est une science qui a quelques décennies d’histoire. Les utilisateurs ont donc eu le temps de s’habituer à certaines caractéristiques, en termes d’ergonomie, de fonctionnalités, mais aussi d’utilisabilité et de temps de réponse.
Les nouveaux services (Web, Wap, nomadisme…) ont une offre fonctionnelle très riche. Leur utilisabilité est perfectible, mais un peu de discipline et certaines pratiques (cf thèmes abordés dans les sections précédentes) devraient répondre à ce besoin.
En termes d’interactivité, certains utilisateurs (de gros systèmes en particulier) ont l’habitude de temps de réponse du système assez courts. Mieux, certains ont tellement l’habitude de leurs logiciels (agences de voyage, banques, assurances) qu’ils connaissent les « écrans » et leur enchaînement par cœur, ce qui accélère énormément leur saisie (dans ce genre de système, le logiciel client mémorise toutes les frappes de touches, on peut donc taper des caractères qui apparaîtront seulement dans l’écran suivant : l’utilisateur avancé n’est jamais en attente du système).
Il faudra concevoir une architecture permettant à nos systèmes d’être aussi réactifs que les systèmes connus de certains utilisateurs, tout en assurant les fonctionnalités et l’ergonomie qui font le succès des nouvelles technologies. Typiquement, certaines informations peuvent être préparées de manière asynchrone, et être immédiatement disponibles lors de leur demande par l’utilisateur. Un site permettant la pagination des résultats d’une requête peut, par exemple, tirer parti de la probabilité que l’utilisateur demande la « prochaine » page ou la « précédente » pour préparer ces informations alors que l’utilisateur prend connaissance de la page « courante » (dans un thread asynchrone côté serveur).
Struts est un framework Open Source (voir ressources) qui répond à la plupart des besoins fondamentaux que nous avons abordés précédemment, en utilisant Servlets et JSP. Il cristallise les meilleures pratiques de conception orientée objet appliquées au développement de sites Web, en s’appuyant énormément sur la notion de Design Pattern. D’autres frameworks adoptent des approches voisines, en ajoutant souvent des outils d’aide à la configuration (description graphique de la navigation, internationalisation optimisée, cache de pages dynamiques…).
Nous nous proposons dans cette section d’examiner l’architecture typique d’un site basé sur Struts, ainsi que le déroulement d’une séquence utilisateur.
Dans un souci de séparation des responsabilités, Struts nous invite tout d’abord à appliquer le modèle MVC (Model-View-Controller) :
· Contrôleur : une servlet (fournie par Struts et très configurable) est responsable de traiter toutes les requêtes de l’utilisateur.
· Modèle : des composants JavaBeans représentent l’espace de travail en mémoire, stockent les informations de la requête de l’utilisateur, et sont un point d’entrée vers les services métier ou les données du système
· Vue : toute présentation dynamique passe par l’utilisation des JSP. Les JSP sont complètement passives, et se bornent à présenter l’espace de travail stocké en mémoire par les JavaBeans, lorsque la servlet leur en donne l’ordre.
Le synoptique de traitement d’une requête utilisateur est donc le suivant :
Lorsque le client formule une requête, il s’adresse systématiquement à la servlet contrôleur, qui est unique (elle constitue la Façade de notre système). Celle-ci analyse la requête du client, et l’oriente vers un traitement adéquat. Le résultat du traitement produit généralement des informations, volatiles, stockées sous la forme de JavaBeans. La servlet passe ensuite la main à une page JSP chargée de réaliser le rendu des JavaBeans dans le langage de présentation adéquat (typiquement, HTML dans cet article). Mais cela pourrait bien sûr être WML, VoiceXML, SVG…. Les JSP ont donc dans Struts les responsabilités de restitution, d’internationalisation, de multi-canal, et de personnalisation.
Voyons ensuite comment Struts propose de traiter l’aide à la correction des erreurs de l’utilisateur dans un formulaire. Il suffit pour cela d’associer une classe Java (un « FormBean ») dont les attributs reflètent les champs du formulaire. Cette classe disposera également d’une méthode d’auto-validation, ainsi que d’un attribut (une table d’association) permettant de mémoriser les erreurs de chaque champ.
|
|
|
Struts s’appuiera sur notre classe pour déterminer si les saisies de l’utilisateur sont correctes (validation de surface, éventuellement complétée par une vérification d’existence lorsqu’il s’agit d’informations référençant des données métier : numéro de produit, etc…) : à la réception de la requête du client, la servlet de Struts positionne les propriétés du FormBean aux valeurs saisies par l’utilisateur, et demande au Bean de se valider. S’il est valide, on passera à l’étape suivante dans la navigation du site. Si ce n’est pas le cas, la servlet réoriente l’utilisateur vers la JSP qui avait généré son formulaire, et demande à cette dernière de réafficher le même formulaire, tout en
· Affichant, face à chaque champ, les messages d’erreurs correspondants (messages que stocke le Bean, souvenez-vous)
· Positionnant les valeurs des champs saisis précédemment à leurs anciennes valeurs (attributs du Bean)
Le FormBean a donc deux rôles, celui de la validation de surface et de l’indication des erreurs, et celui de mémorisation des valeurs précédemment saisies par l’utilisateur (on peut donc voir ici l’application du pattern Memento).
Reprenons graphiquement le déroulement des événements : le client se connecte une première fois et demande à accéder au formulaire d’une fonctionnalité particulière.
Après avoir rempli le formulaire, le client le soumet. Malheureusement, il a commis une erreur lors de la saisie. Donc on lui re-présente son formulaire pré-rempli, en lui indiquant les erreurs commises.

Une fois les erreurs corrigées, le client soumet à nouveau le formulaire. Pour peu qu’il n’ait pas fait d’erreur, cette fois-ci, sa requête va franchir l’étape de validation et déclencher une action sur le système (cf ci-après).
Jusqu’ici, nous avons passé sous silence le mécanisme permettant à la servlet de savoir quel FormBean était associé à chaque requête utilisateur ; poussons donc plus avant le raisonnement. En réalité, le FormBean ne répond qu’à une partie du problème, car l’utilisateur ne souhaite pas seulement valider ses entrées, mais également (c’est son objectif) exécuter des actions sur le système à travers son interface web (achat en ligne, recherche d’informations…). Donc il y aura lieu d’associer au FormBean une « fonctionnalité » (qui bien entendu ne sera déclenchée que si le FormBean valide les saisies de l’utilisateur). Celle-ci pourrait être une méthode du Bean, mais dans un souci de séparation des responsabilités, nous allons l’externaliser dans une classe distincte, que nous appellerons de manière générale une « commande » (vous reconnaîtrez à nouveau un Design Pattern célèbre).
Dans le framework Struts, c’est toujours la servlet contrôleur qui joue le rôle de « routeur » de requêtes client. Le client qui s’adresse au système va demander à exécuter une action particulière (« s’authentifier », « rechercher une info »…) ; cela se concrétise par un paramètre HTTP envoyé du client à la servlet contrôleur : http://……/controleur?action=authentifier.
· Sur réception de cette requête, la servlet recherche (dans un fichier de correspondances) le FormBean associé à cette requête, positionne ses attributs aux valeurs saisies par l’utilisateur, et demande au Bean de se valider
· Si la validation échoue (l’utilisateur s’est trompé sur au moins un des champs du formulaire, ou bien il a tenté d’accéder directement à l’exécution du service sans passer par le formulaire et donc les champs ne sont pas renseignés), le contrôleur passe immédiatement la main à la page JSP responsable de présenter (à nouveau) le formulaire d’accès au service. Ce formulaire est enrichi des anciennes valeurs saisies par l’utilisateur lors de sa précédente requête (les TagLibs de Struts rendent ceci automatique), ainsi que des messages d’erreur qui vont inviter l’utilisateur à corriger certains champs de ce formulaire. Cette boucle de « rétro-action » (client-servlet contrôleur-Bean-JSP-client) est sans fin, jusqu’à ce que le client saisisse des informations valables.
· Lorsque la validation se passe bien, la servlet contrôleur décide d’aller plus loin et d’exécuter la « commande » associée au bean et à la requête client. Cette classe est responsable de rendre le service souhaité, et part du principe que les données d’entrée sont correctes (puisqu’elles ont été validées par le FormBean).

Pour finir, Struts propose un certain nombre de « TagLibs », des balises spécifiques supplémentaires permettant de bâtir aisément les aspects répétitifs de la couche de présentation (dans les JSP) tels que les tableaux, listes…
Ces balises permettent également l’internationalisation automatique des pages. Les JSP installées dans Struts ont tout intérêt à utiliser ces TagLibs qui traduisent automatiquement des libellés abstraits (texte qui doit apparaître dans les pages Web) en un texte concret dans la langue préférée de l’utilisateur. Cette information linguistique est obtenue de manière transparente, car elle est portée par toute requête HTTP (plus précisément, par le header « accept-langage »).
Struts est un framework assez complet, et propose une architecture purement objet, truffée de Design Patterns. Un site reposant sur cet outil est de ce fait très extensible et aisément maintenable. La prise en main initiale peut toutefois paraître un peu ardue, en particulier aux personnes qui ne sont pas familières des Patterns.
On peut regretter, toutefois, que Struts ne permette de façon standard que de valider les saisies utilisateur côté serveur. En effet, une partie de cette validation gagnerait à être réalisée côté client : un champ non rempli, un format de date non respecté, peut être détecté immédiatement, et donc améliorer la réactivité du système. Cette validation côté client diminuerait également le nombre de requête incorrectes, et donc le nombre de hits sur le serveur.
Il manque également à Struts les notions de pagination, sous la forme de TagLibs supplémentaires par exemple, ainsi que les optimisations du type « thread d’accès aux données proactif ». Cette optimisation consiste, dans un système de pagination (moteur de recherche par exemple), à créer un thread actif dans la session d’un utilisateur, chargé de maintenir un tableau « assez rempli » de réponses à la requête de l’utilisateur. Lorsque l’utilisateur passe à la page suivante, la couche de présentation (ici, une JSP) pourrait très bien travailler sur un tableau en mémoire dans la session ; le résultat serait donc immédiat et l’utilisateur l’obtiendrait en un temps record. Puis, lorsque les données du tableau sont pratiquement toutes consommées (si le prochain accès risque de causer un manque de données), le thread proactif décide spontanément de ré-exécuter la requête utilisateur sur l’engin de stockage et de charger en session les prochaines données. L’utilisateur n’est donc jamais en attente à cause d’un accès base de données puisque ces accès se font lors de la « période de réflexion », ou de lecture, de l’utilisateur.
Enfin, le principal reproche que l’on peut faire à la partie « vue » de Struts est l’utilisation abusive de TagLibs. En effet, une page qui tire parti au maximum des balises étendues de Struts devient complètement illisible pour une personne ne connaissant que le langage HTML. En voulant trop extraire le code des pages JSP, Struts est tombé dans cette aberration qui fait qu’un designer Web ne peut plus vraiment concevoir les pages de rendu : il doit apprendre les balises Struts, ou communiquer énormément avec une personne plus technique. Un outil graphique qui comprendrait à la fois le langage HTML et les TagLibs Struts serait bienvenu, mais cela n’est pour l’instant que de la prospective. Donc le problème risque de persister un moment.

WebForms aborde la couche de présentation d’une manière originale, atypique par rapport aux « bonnes pratiques de conception objet » que l’on peut rencontrer dans la plupart des frameworks Java.
En effet, le développement de sites Web basés sur les WebForms s’approche davantage de la programmation d’interfaces graphiques typiques (Formulaires Visual Basic par exemple) que de la programmation Web classique (traitement d’une requête, génération de la prochaine page).
Les WebForms sont des composants actifs proposés par l’environnement ASP.NET que l’on peut ajouter à un environnement de conception de page Web de manière graphique, comme si l’on concevait une interface client lourd VB ou C#. Ces éléments peuvent être classés de la manière suivante :
· les éléments classiques que l’on peut trouver dans un formulaire HTML (bouton, label, zone de texte, combo box, radio, check…)
· les composants utiles, mais plus complexes à créer dans le monde Web (calendrier graphique HTML, bannière publicitaire dynamique)
· les outils de « contrainte » permettant de valider les informations saisies par l’utilisateur.
La bonne surprise, lorsqu’on prend en main ce framework, vient du fait qu’il ne repose en rien sur des composants « contrôles ActiveX », qui posent d’énormes problèmes de déploiement et de sécurité. Non, les WebForms sont des composants exécutés côté serveur Web (IIS en l’occurrence), et génèrent des pages 100% standard contenant à la fois HTML et JavaScript. Cela va même plus loin : les WebForms détectent le type de navigateur qui se connecte, et génèrent une page (HTML + JavaScript) compréhensible par ce navigateur en procédant aux adaptations nécessaires (balises non supportées, etc…). Donc ce framework fait preuve d’une grande ouverture vis-à-vis de ses clients Web en s’adaptant à celui-ci. Cette précision étant donnée, reprenons notre étude de la couche de présentation, cette fois en utilisant les WebForms.

La création de pages Web avec Visual Studio .NET est très simple, et l’on peut aisément passer du mode texte HTML à un mode graphique très convivial. Mais l’innovation réside dans la manière dont les WebForms nous proposent de traiter les différents événements générés par l’utilisateur (soumission d’un formulaire, click sur un bouton…) : il suffit de double-cliquer sur un composant actif de notre page dans le mode conception pour se retrouver dans une méthode « callback », un gestionnaire d’événement (cette pratique rappellera des souvenirs aux développeurs d’IHM VisualBasic). Par exemple, la réaction à un click bouton sera décrite dans une méthode du type :
Private Sub Button1_Click(ByVal
sender As System.Object,
ByVal e As System.EventArgs) Handles
Button1.Click
‘ Votre code “callback” apparaît ici
‘ et sera exécuté côté serveur
End Sub
Cela vous semble obscur, ou magique ? Décortiquons ensemble le fonctionnement de ce mécanisme. Chaque page ASP.NET peut contenir du code HTML, des WebForms, ainsi qu’une référence à une classe VB.NET ou C# que l’on qualifie de « CodeBehind ». Cette classe renferme un ensemble de méthode, et la page ASP.NET peut :
· soit invoquer une méthode de cette classe CodeBehind pour simplifier sa tâche (un calcul un peu complexe gagne à être implémenté dans le CodeBehind et invoqué par une instruction de Script dans la page ASP.NET)
· soit router les événements qu’elle reçoit (qui proviennent des utilisateurs, par le biais de leurs navigateurs Web) vers les méthodes adéquates
Typiquement, l’événement « click bouton 1 » généré par l’utilisateur se transforme en une requête HTTP à destination de la page ASP.NET contenant le bouton en question. La page reçoit cette requête, l’interprète et comprend qu’il s’agit d’un événement click sur le bouton numéro 1, et invoque donc la méthode « Button1_Click » sur la classe CodeBehind.
Cette pratique est à la fois simple (un simple double click permet d’installer notre code gestionnaire d’événement), et assez propre ; le code de gestion ne se trouve pas dans la page ASP, mais dans la classe CodeBehind : les responsabilités de traitement et de présentation sont donc assez bien séparées.

Mais continuons notre évaluation du framework WebForms par rapport aux responsabilités génériques afférentes à la couche de présentation. En particulier, comment gérer le contrôle des saisies utilisateur en WebForms ? Eh bien c’est déconcertant de simplicité : il suffit de glisser-déposer, en mode conception, un « composant de validation » sur la page ASP.NET. Il existe un panel assez complet de WebForms de validation : RequiredFieldValidator, CompareValidator, RangeValidator, RegularExpressionValidator et CustomValidator. Chaque instance de composant de validation possède un attribut qui indique sur quel champ de formulaire doit s’opérer la validation, ainsi qu’un message d’erreur qui s’affiche lorsque la validation échoue. Au niveau de l’exécution, les choses sont simples :
· lorsqu’une ASP.NET génère une page HTML, l’utilisateur est invité à en remplir les champs, et à soumettre la page (à la même ASP.NET)
· sur réception d’une requête, l’ASP.NET exécute immédiatement tous les composants de validation. Si le moindre problème est détecté (champ non rempli, etc…), la page Web est reconstruite pratiquement à l’identique, à ceci près que les messages d’erreur des composants de validation deviennent visibles. L’utilisateur est donc invité à nouveau à saisir les informations du formulaire. Comme dans Struts, les champs précédemment saisis par l’utilisateur et qui n’ont pas généré d’erreur de validation sont automatiquement remplis (aux mêmes valeurs) par les WebForms.
· enfin, si la page soumise par l’utilisateur est correcte au sens des contraintes de validation (et dans ce cas uniquement), tous les événements sont déclenchés dans la classe CodeBehind. Cela permet de réagir très simplement à une modification de texte dans une TextBox (événement du type « TextBox1_TextChanged »), un click sur un bouton, un changement d’état d’un bouton Radio ou CheckBox… exactement comme on a l’habitude de le faire dans un développement de client lourd type VisualBasic.
Le gros avantage des WebForms, comparé à un framework Java type Struts, réside dans la possibilité de concevoir graphiquement nos pages Web. En effet, VisualStudio.NET est capable de rendre graphiquement à la fois les balises HTML et les WebForms, ce qui est impossible (pour l’instant) avec les TagLibs JSP (sauf à utiliser des TagLibs propriétaires à un éditeur d’environnement de développement Java, ce qui n’est probablement pas une bonne idée).