Chez Jérémie

Chez Jérémie, parfois c'est sérieux, parfois non !

Rendre une application AJAX bookmarquable

- Web - Lien permanent

Parmi les reproches communément adressés aux applications AJAX on retrouve un grief assez récurrent : l'impossibilité d'utiliser les favoris. Dans cet article, je vais vous expliquer comment gérer les favoris (en anglais "bookmarks" d'où l'horrible néologisme du titre) avec une application utilisant AJAX. C'est beaucoup plus simple qu'il n'y paraît.

Beaucoup d'architecture

Avant même de penser à coder, il va falloir commencer par se poser un certain nombre de questions de nature architecturales. En effet, l'utilisation d'AJAX "pervertit" l'usage normal que l'on peut avoir d'un navigateur. Traditionnellement, un navigateur charge des documents (on parle aussi de ressources) identifiés par leur URL via le protocole HTTP le plus souvent. Le navigateur permet de passer d'un document à l'autre (via des liens hypertexte) ou éventuellement de modifier le document (via des formulaires) et d'en charger l'intégralité de la version modifiée. AJAX distord ce fonctionnement. Avec cette technique, le navigateur charge un seul document et va en modifier certaines parties en fonction des actions de l'utilisateur.

Entre ces deux approches, les mécanismes techniques mis en œuvre sont fondamentalement les mêmes, mais c'est leur mise à disposition à destination de l'utilisateur qui diffère. D'un côté, l'utilisateur accède à N documents et de l'autre, il accède à un seul document qui se modifie en permanence. C'est sur ce dernier point que réside la difficulté liée aux favoris : ce que l'on met en favoris, c'est l'URL d'une ressource !

La notion de contexte

HTTP se caractérise par une absence de contexte. C'est cette spécificité qui permet de mettre en favori n'importe quelle URL. En effet, sur cette base, une URL pointera en théorie toujours sur le document que l'on a vu. Le problème si vous mettez en favori une application AJAX, c'est que vous ne mettrez en favori que l'état initial du document, et pas l'état dans lequel se trouve votre document au moment où vous enregistrez votre favori.

Donc, comme on le voit, AJAX créée un document avec un contexte : l'état dans lequel il est à un instant T. Et ça, les navigateurs web ne savent pas le gérer nativement.

Une application AJAX a donc deux problèmes à résoudre :

  1. L'application doit disposer d'un mécanisme permettant de définir le contexte courant du document et l'exploiter le cas échéant.
  2. Les navigateurs ne savent mettre en favoris que des URLs
Cela signifie donc que pour rendre un application AJAX bookmarquable, le contexte du document doit être stocké dans l'URL. Or, comment modifier une URL sans recharger le document (ce qui ferait perdre tout le bénéfice d'AJAX) ?

La structure d'une URL

La structure des URLs va nous aider à voir où l'on peut stocker des paramètres de contexte. De manière simplifiée, une URL est structurée comme suit :

  1. Le protocole : En général HTTP dès que l'on est dans un cadre Web.
  2. Le domaine : Il est de la forme sous-domaine.domaine.extension. Il permet d'identifier la machine à qui on va demander le document qui nous intéresse.
  3. L'emplacement du document : Il est de la forme /dossier/sous-dossier/fichier
  4. Les paramètre de requête : Ils sont de la forme ?paramètre1=valeur1&paramètreN=valeurN. Ces paramètres peuvent influer sur la nature profonde d'un document ou sur son contenu.
  5. Une éventuelle ancre nommée : Elle est de la forme #mon-ancre. Elle a pour vocation de cibler un élément identifié dans le document et, le cas échéant de positionner cette partie du document dans la zone visible de la fenêtre du navigateur.
Assez instinctivement, on serait tenté de positionner nos informations de contexte en tant que paramètre de requête. Effectivement, si on met en favori une URL avec des paramètres permettant de connaitre le contexte, on aura bien l'effet attendu. Malheureusement, si vous modifiez les paramètres de requête de l'URL, un navigateur va envoyer une requête HTTP et recharger la page, ce qui va à l'encontre de ce que l'on veut puisqu'on est en AJAX.

La gestion des URL par les navigateurs

De manière générale, toute modification de l'URL du navigateur conduit le navigateur à lancer une requête HTTP afin d'obtenir la dernière version du document correspondant. C'est aussi vrai pour les paramètres de requête. Après tout, si on modifie les paramètres, il faut bien que celle-ci soit relancée afin que le serveur puisse fournir le document correspondant.

Il y a néanmoins une exception à cette règle : la modification de l'ancre nommée.En effet, pour le navigateur, l'ancre nommée représente un élément présent dans le document courant et qu'il faut atteindre. En conséquence, on ne change pas de ressource (contrairement à une modification des paramètres de requête), mais on cherche à atteindre un point de la ressource courante il n'est donc pas nécessaire de lancer une nouvelle requête HTTP, au contraire, ce serait contre productif.

Alléluia, c'est donc là que se situe le salut du développeur d'application AJAX : utiliser l'ancre nommée pour stocker le contexte de son application dans l'URL.

Oui mais...

Il y a contexte et contexte. On pourrait philosopher pendant des heures et des heures sur ce détournement des ancres nommées dans un contexte AJAX. D'un point de vue extrêmement prosaïque, c'est la seule solution qui marche si on veut pouvoir utiliser les favoris du navigateur. Par contre, ce n'est pas parce que ça marche qu'il faut faire tout et n'importe quoi. Placer l'intégralité d'un contexte applicatif à cet endroit n'est pas pertinent et potentiellement dangereux pour votre application : un utilisateur peut modifier cet élément de lui-même, on ne peut donc pas lui faire confiance. Ainsi, si vous placez un ensemble de variables en clair à cet endroit, vous vous exposez à des risques d'attaque de votre application. Il existe des tas de façons de stocker les données du contexte applicatif, que ce soit localement (les mécanismes "Session Storage" ou "Local Storage" de HTML 5 ou leur succédané des divers frameworks Javascript) ou sur un serveur distant (beaucoup plus sûr architecturalement parlant, mais plus problématique pour gérer la notion de "offline"). N'utilisez les ancres nommées que pour stocker un identifiant du contexte applicatif. Vous en retirerez des avantages pour vous et pour vos utilisateurs :

  • Pour vous : vous pouvez renforcer facilement la sécurité de vos applications tout en pouvant enrichir votre contexte applicatif sans changer vos mécanismes de gestion des URL.
  • Pour votre utilisateur : son URL reste lisible (voire compréhensible si vous gérez bien vos identifiants de contexte) et relativement légère sans exposer ses données.

Un peu de code

Maintenant que nous avons vu les bases théoriques, comment mettre ça concrètement en œuvre ?

window.location est mon ami

Vous faites de l'AJAX, donc vous faites du Javascript, et si vous faites du Javascript, vous avez accès à l'objet window.location. Cet objet vous donne accès à l'URL courante du navigateur et vous permet de la modifier.

Lire l'URL courante : var URL = window.location.href

Lire l'ancre nommée (elle commencera par le caractère #) : var ANCRE = window.location.hash

Modifier l'ancre nommé :

/* Récupération du contexte actuel */
var AncreOLD = window.location.hash;

/* Récupération de l'URL courante */
var URL = window.location.href;

/* On supprime l'ancienne ancre de l'URL */
URL = URL.substring(0, URL.length - AncreOLD.length);

/* On créée une nouvelle ancre */
var AncreNEW = Math.round(Math.random()*1000);

/* On met à jour l'URL dans la barre d'adresse du navigateur */
window.location.replace(URL + '#' + AncreNEW);

Le petit script ci-avant vous montre les étapes clés de la modification de l'URL du navigateur. A vous de l'adapter à votre propre contexte applicatif, mais basiquement, tout est là.

Et voila !

Oui, oui, ce n'est pas plus compliqué que ça. Avec ce que vous avez vu là, vous êtes capable de rendre vos applications AJAX bookmarquable. Ceci dit, maintenant, il vous reste à faire le plus compliqué : gérer votre contexte au niveau applicatif. Sur ce point, chaque application a ses spécificités et ses besoins. N'hésitez pas à aller voir ce qu'ont fait d'autres développeurs à ce sujet. La première chose à regarder c'est ce qu'offrent les framework Javascript pour gérer cela nativement. Ensuite, si vous en avez le courage, vous pouvez aller voir ce que font des gens comme Google (Gmail, Reader et Wave sont extrêmement instructifs à cet égard... mais c'est horriblement touffu !).


Un gros merci au deux bisounours d'amour que sont Virginie et Stéphane pour le sauvetage orthographique de ce billet :)