Applications asynchrones AJAX et XMLHttpRequest

Applications synchrones

Action → Requête → Réponse HTML → Action → …

  1. L’utilisateur demande une URL

    GET /action?parametres HTTP/1.1
    ...
    
  2. Le server répond avec du HTML.

  3. L’utilisateur quitte la page en

    • Cliquant un lien,
    • Cliquant un bouton de type submit,
    • rafraîchissant.
    POST /autre_action?parametres HTTP/1.1
    ...
    
  4. Le server répond avec de l’autre HTML.

Et l’état ?

HTTP est sans état

Dans le Web 1.0, le server est le seul responsable du maintien de l’état

  • dans la logique de l’application (URLs, requêtes, etc.)
  • dans son stockage local (sessions, bases de données),
  • chez le client (cookies).

Action → Requête → Réponse HTML → …

Dommage collatéral : le browser perd l’état (à l’exception de son stockage local) à chaque nouvelle action.

Démonstration : naviguez vers la racine, puis revenez.

Temps passé sur ces slides :

Description du modèle de flux de données du Web 1.0, par Jesse J Garret

Exemple synchrone

Ask StackOverflow :

<form method='GET' action='http://stackoverflow.com/search'>
  
  <input name='q' type='text' value='AJAX' />
  <input type='submit' value='Ask' />
</form>

Description of the AJAX dataflow model, by Jesse J Garret

Exemple asynchrone

Ask StackOverflow :

Quels éléments pour une navigation asynchrone ?

Action ≠ Requête

  • JavaScript intercepte les actions (évènements) de l’utilisateur.

Requêtes asynchrones (XMLHttpRequest, Fetch API)

  • JavaScript peut initier une requête indépendamment des actions de l’utilisateur,
  • Les requêtes n’interrompent pas la navigation.

Server push (EventSource, Web sockets)

  • Le serveur peut envoyer des données au client sans attendre une requête.

XMLHttpRequest

Introduit par Microsoft dans IE5, maintenant un standard W3C.

  • Envoie des requêtes POST ou GET (ou autre) vers le server ;
  • Ne bloque pas le browser en attendant la response ;
  • Exécute une callback asynchrone à l’arrivée de la response.
/***************  Cliquez !  ***************/
mydiv.onclick = function() {
  var xhr = new XMLHttpRequest();
  xhr.open("GET", "../LICENSE");
  xhr.onload = function(event) {
    alert(xhr.response);
  }
  xhr.send();
}

Creation et préparation

var xhr = new XMLHttpRequest();
xhr.open("POST", "http://.../action?params");

Callbacks

xhr.onload = function(event) {
  console.log('Succes');
}
xhr.onerror = function(event) {
  console.log('Erreur');
}
xhr.onabort = function(event) {
  console.log("Annulé par l'utilisateur");
}
xhr.onprogress = function(event) {
  console.log('Téléchargement...');
}

Envoyer des données

xhr.setRequestHeader('Content-Type', 'text/plain')
xhr.send("Hello world !");

Simuler un formulaire (peut aussi envoyer des données binaires)

var formData = new FormData();
formData.append('q', 'AJAX');
formData.append('hl', 'en');
// Content-Type: multipart/form-data  par defaut
xhr.send(formData);

Envoyer du JSON

var data = { primes : [2, 3, 5, 7],
             even   : [2, 4, 6, 8] };
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(data));

Lire la réponse

xhr.onload = function() {
  console.log(xhr.responseText);  // Texte simple
  console.log(xhr.responseXML);   // XML (si la réponse est dans ce format)
  console.log(xhr.response);      // Configurable (texte par défaut)
}

Pré-traitement de la réponse par le browser

xhr.responseType = "json";
xhr.onload = function() {
  var obj = xhr.response;         // transformé en objet JavaScript
  console.log(obj.toto);          // par le browser
}
  • responseType = "text" : texte (default),
  • responseType = "document" : arbre DOM d’un document HTML,
  • responseType = "arraybuffer", responseType = "Blob" : données binaires.

Étude de cas : API StackOverflow

document.querySelector('#SO').onsubmit = function(e) {
var query = encodeURIComponent(document.querySelector('#query').value);
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.stackexchange.com/2.2/search/advanced' + '?q=' + query + '&site=stackoverflow');
xhr.onload = callback;
xhr.responseType = 'json';
xhr.send();
e.preventDefault();
}
  • Récupération du contenu du champs de texte ;
  • Échappement des caractères spéciaux ;
  • Préparation de la requête à https://api.stackexchange.com ;
  • On attend un résultat au format JSON ;
  • On arrête la soumission du formulaire.

Étude de cas : API StackOverflow

var callback = function(e) {
  if (xhr.response && xhr.response.items) {
    var liste = xhr.response.items;
for (var i = 0; i < liste.length; i++) { document.querySelector('#answers > ul').innerHTML = '<li>' + liste[i].title + '</li>'; }
} else { document.querySelector('#answers').innerHTML = '<p>Pas de résultats.</p>'; } }
  • La réponse (au format JSON) est automatiquement convertie en objet JavaScript ;
  • On construit une liste des questions répondant aux critères ;
  • On insère le résultat dans la page via le DOM ;
  • Voir la documentation complète de l’API : https://api.stackexchange.com/docs/.

Lectures

Fork me on GitHub