Un bon « MVC Web » doit garder à l’esprit que HTTP est partie prenante du contrôleur (au même titre que le navigateur Web également). La base est donc de faire du dispatch selon l’URL de la requête, ainsi bien entendu que la méthode HTTP utilisée (GET pour les requêtes safe, POST, PUT et/ou DELETE pour les actions, par définition unsafe). La seconde règle est de ne pas confondre redirection et vue : pour simplifier, si une action de suppression ne doit pas résulter en un écran “suppression effectuée”, la réponse HTTP devrait être une redirection (le navigateur va alors faire une requête GET sur cette autre URL, et ainsi récupérer par exemple une version “à jour” des données, ne contenant plus l’élément supprimé).

Quel que soit le framework employé, il ne faut pas perdre de vue que dans une application Web, la phase de conception est très importante, puisqu’elle doit définir les échanges client/serveur. Cette phase de conception est d’autant plus importante si on utilise de l’AJAX en complément des mécanismes HTML (liens et formulaires).

# Le cas Ruby on Rails: une inspiration pour tous

Même si les développeurs de RoR n’ont rien inventé (beaucoup avaient déjà utilisé des patterns similaires), ils ont su imposer leur vision du MVC Web, totalement “Web style” (ce n’était pas le cas dans les premières versions de RoR cependant, où la méthode GET pouvait très bien être utilisée pour des actions unsafe, d’où de nombreux problèmes, notamment avec le Google Web Accelerator), et qui inspire quasiment tout le monde aujourd’hui: TurboGears et Django dans le monde Python, Symfony côté PHP, Phobos et Spring MVC en Java, et ASP.NET MVC pour la plateforme .NET.

Avec Rails, il est éventuellement possible d’utiliser le rendu d’une vue là où une redirection serait plus appropriée, mais ce fonctionnement demande au final plus de travail que de réaliser une redirection (dû au fait que les données –le modèle– doit être transmis à la vue –plus ou moins explicitement–).

À noter tout de même, RoR ne doit pas sa popularité qu’à son MVC, mais également à sa vision convention over configuration.

# Le cas JSF: l’exemple à ne pas suivre

JSF est un mauvais MVC Web: les spécifications de JSF indiquent de toujours utiliser la méthode POST, et les outcomes n’utilisent jamais de redirection. En pratique, cela signifie que :

  • rien n’est jamais bookmarkable (utilisation de POST)
  • l’adresse apparaissant dans la barre d’adresse du navigateur est toujours “en retard” par rapport au contenu affiché (donc si on rafraîchit la page –F5, sans renvoyer les données POST–, le résultat sera différent).

Le problème est dans la conception même de JSF, et est en partie lié à son orientation composant: une URL est lié à une page JSP, dont les composants déterminent comment évaluer la requête POST, et appellent une voire plusieurs actions liées au composant ayant déclenché la requête ainsi qu’aux autres composants dont la valeur a été modifiée, la réponse du serveur étant le résultat d’une autre page JSP, qui n’a potentiellement rien à voir avec l’URL de la requête. Il n’y a donc pas de mapping URL/contrôleur, mais URL+requête-POST/action(s).

De plus, JSF est par essence totalement stateful, ce qui va à l’encontre des fondements du Web et de HTTP (pensez ReST).

Pour les curieux, le problème se situe dans le troisième bloc de la figure 4.2 du document Designing Enterprise Applications with the J2EE(TM) Platform, §4.4 Web-Tier Application Framework Design : la sélection de la vue “suivante”; et on nous indique que cette sélection dépend de la vue “courante”: il n’y a pas de “vue courante” en mode déconnecté comme avec HTTP, et la “vue” renvoyée n’est pas sensée être l’état de “l’application” (si tant est que ce concept existe) mais une représentation de la ressource identifiée par l’URL de la requête et/ou une représentation du résultat de l’action (avec le statut HTTP correct correspondant).

L’orientation “composants” de JSF fait que, de par sa conception, les vues vont chercher l’information nécessaire dans les composants (qui servent donc à la fois de modèle et/ou de contrôleur), alors que ça devrait être au contrôleur de donner l’ensemble des informations nécessaires (modèle) à la vue. Un autre gros souci de JSF est qu’il n’est vraiment pas aisé ni intuitif de faire des redirections en réponse à une requête, en tous cas lorsqu’on veut diriger vers un URL dynamique dépendant de l’action en cours.

# Alfresco : de JSF aux Web Scripts

Alfresco a commencé avec un client JSF (qui par ailleurs reposait sur le pire de JSF), rapidement complémenté par quelques servlets (qu’on pourrait presque qualifier de hacks) permettant un accès direct à certains écrans.

Depuis la version 2.1, Alfresco nous a gratifié d’un nouveau mode de développement « léger » : les Web Scripts. Ce « framework » permet de développer des applications ReSTful (ou autre) dans Alfresco. Chaque Web Script se compose d’un fichier de description, d’un script (facultatif) et d’un ou plusieurs modèles de rendu.

  • Le fichier de description indique les URLs par lesquelles le Web Script est accessible (similaire à Routes de Ruby on Rails, mais avec une gestion décentralisée), le mode d’authentification (inexistant –guest–, n’importe quel utilisateur Alfresco, ou un administrateur uniquement), et depuis peu des options de cache HTTP de la réponse.
  • Le script (JavaScript) permet de valider les paramètres, modifier l’entrepôt de contenus, etc. et éventuellement changer le statut HTTP de la réponse (redirection, erreur, etc.)
  • Les templates (FreeMarker) correspondent aux représentations possibles, avec un template par format (HTML, JSON, Atom, etc.)

Alfresco utilise également le principe « convention over configuration » :

  • Tous ces fichiers ne sont liés entre eux que par leur nom. Celui-ci doit de plus respecter un format particulier et contenir la méthode HTTP gérée (un Web Script ne répond qu’à une méthode HTTP), ainsi que le format généré (pour les templates).
  • Le choix du format de la réponse (et donc du template utilisé) est réalisé par un paramètre format dans la query-string ou par un suffixe (à la façon des extensions de fichiers Windows).
  • Le script communique avec les templates à l’aide d’une variable globale model.
  • Si la réponse n’est pas 200 OK, le script peut demander à utiliser un autre template pour le rendu. Ce template doit faire partie des fichiers du Web Script mais contenir le code HTTP dans son nom. À défaut, un fichier nommé status.<code>.<format>.ftl sera utilisé, recherché également dans les répertoires parents. Au pire, un fichier status.<format>.ftl peut être placé à la racine des Web Scripts de façon à servir de catch-all.

Tout ceci est encore grandement perfectible (pas de génération d’URL pour un autre Web Script, à la façon des link_to de Ruby on Rails, matching des URLs simplistes) mais la base est là, et est remplaçable, comme tout composant d’Alfresco.

Alfresco migre d’ailleurs vers un client léger entièrement basé sur les Web Scripts pour sa future version 3.0, et a pour cela réécrit le framework (dont le matching des URLs) et l’a beaucoup enrichi par ailleurs (notion de composants, intégration de Yahoo! UI, etc.)