AJAX§

Principe§

Jusqu’à maintenant...§

  • Les applications côté client que nous avons écrites sont intégralement chargées avec la page HTML.
  • Toute communication avec le serveur se fait par rechargement de la page...
  • ... et donc réinitialisation de l’application (ou d’une autre).

Ce serait bien si...§

  • Nos applications pouvaient communiquer avec le serveur,

  • sans avoir à se recharger totalement :

    • conservation de l’état de l’application
    • fluidité pour l’utilisateur
  • Mais cela suppose que le code Javascript ne se bloque pas en attendant la réponse du serveur

    → appel asynchrone (événements)

Définition§

AJAX signifie

  • Asynchronous
  • Javascript
  • And
  • XML

... mais en pratique, on peut échanger n’importe quoi avec le serveur, pas uniquement du XML.

Exemple simple§

var req = new XMLHttpRequest();
req.open("GET", url);
req.onerror = function() {
    console.log("Échec de chargement "+url);
};
req.onload = function() {
    if (req.status === 200) {
      var data = JSON.parse(req.responseText);
      // do what you have to do with 'data'
    } else {
      console.log("Erreur " + req.status);
    }
};
req.send();
  • La fonction affectée à onerror est appelée si la requête échoue.
  • La fonction affectée à onload est appelée lorsque la requête aboutit (mais il faut encore vérifier le statut de la réponse).
  • L’attribut status contient le code de retour HTTP indiquant si la requête a réussi (200) ou non (e.g. 404).
  • L’attribut responseText contient le contenu de la réponse.

Note

Les fonctions onerror et onload sont en fait des listeners. On peut également utiliser la notation plus générique :

req.addEventListener('load', function() { /*...*/});

mais dans ce cas précis, où il est peu probable qu’on ait à abonner plusieurs fonctions au même événement, on acceptera la notation plus concise req.onX = ....

Il existe d’autres événements émis par l’objet XMLHttpRequest.

Notez aussi que, dans les anciens navigateurs, tout était géré à travers un unique événement readyStateChange.

AJAX et Sécurité§

Imaginez...§

  • Vous visitez le site pirate.net.
  • La page de garde contient un script qui effectue une requête sur gmail.
  • Comme vous êtes déjà identifié, gmail renvoie une page HTML contenant la liste de vos messages récents.
  • Le script analyse ces données, les renvoie à pirate.net, et bien sûr n’affiche rien de ce qu’il vient de faire.
  • Les pirates connaissent maintenant une partie de vos contacts, et savent de quoi vous parlez avec eux.
  • Mais pirate.net aurait pu tout aussi bien faire une requête sur gmail qui
    • efface la liste de vos contacts,
    • envoie un mail à votre place,
    • change votre mot de passe...
  • Bien sûr, il pourrait également tenter une connexion sur
    • les principaux réseaux sociaux,
    • les sites de banque,
    • etc...

Règles de sécurité§

Pour éviter ce genre d’attaque, XMLHttpRequest possède des limitations :

  • il ne peut pas accéder aux URLs en file:,
  • le code émis par un serveur ne peut se connecter qu’à ce même serveur (Same Origin Policy),
  • ou à un serveur autorisant explicitement les accès par d’autres scripts que les siens (standard CORS).

CORS en deux mots§

  • Lorsqu’un script, provenant d’un serveur srv1, émet une requête AJAX vers un serveur srv2, cette requête contient un en-tête supplémentaire :

    Origin: http://srv1

  • Si le serveur srv2 fait confiance à srv1, il inclut dans sa réponse l’en-tête suivant :

    Access-Control-Allow-Origin: http://srv1

  • ... et le navigateur autorise alors le script à accéder à la réponse.

  • Dans le cas contraire, le navigateur refuse de transmettre la réponse au script. Du point de vue du script, c’est comme si la requête avait échoué.

Note

  • CORS est un standard récent encore peu supporté par les serveurs.
  • Une solution consiste à utiliser un proxy tel que https://crossorigin.me/ .
  • Auparavant, d’autres méthodes ont été proposées pour permettre des accès cross-domain, comme JSONP.

Objets Javascript et JSON§

Principe§

  • Javascript possède une notion d’objet qui se trouve à mi-chemin entre les objets du Java, les structures du C, et les dictionnaires du Python.
  • Contrairement à Java ou C, ces objets peuvent être créés sans classe/type prédéfini.
var p = {
  "nom": "Doe",
  "prénom": "John",
  "age": 42
};

Indice

Les attributs peuvent contenir n’importe quel type, y compris bien sûr des types complexes comme des tableaux ou d’autres objets.

Utilisation§

  • Pour accéder à un attribut d’un objet, on utilise la même notation « pointée » qu’en Java ou en C :

    var n = p.nom ;
    
  • Mais on peut également utiliser la notation « indicée » comme en Python :

    var pr = p['prénom'] ;
    
  • Cette dernière est utile lorsque :

    • le nom de l’attribut comporte des caractères non autorisés, ou
    • le nom de l’attribut est contenu dans une variable.

Note

Cela dit, les caractères accentués (comme dans prénom ci-dessus) sont autorisés. On aurait donc, dans ce cas, pu écrire :

var pr = p.prénom ;

Modification§

p.prénom = "Jane" ;
p.adresse = "42, Main road" ;
delete p.age ;
  • On peut ajouter de nouveaux attributs (ex: adresse)
  • On peut supprimer les attributs (ex: age).
  • Si on tente d’accéder à un attribut inexistant, on obtient la valeur undefined.

JSON§

JSON (Javascript Object Notation) est un sous-ensemble du langage Javascript, utilisé comme format de données sur le Web.

Données supportées par JSON§

  • Objet {}
  • Tableaux []
  • Chaînes de caractères
  • Nombres
  • Booléens
  • null

Note

  • Les chaînes de caractères doivent être entourées par des guillemets doubles (les guillemets simples ne sont pas supportés).
  • Les nom des attributs des objets doivent être entre guillemets doubles.
  • NB: null est supporté, mais pas undefined
  • Des structures complexes peuvent être représentées en JSON : tableaux d’objets, objets contenant d’autres objets...

Format d’échange§

  • Étant directement basé sur Javascript, JSON est bien sûr très utilisé dans ce langage,
  • mais il l’est également dans la plupart des autres langages de programmation, où il a largement remplacé XML (plus simple, plus compact).

Utilisation§

  • JSON.parse prend une chaîne de caractères JSON et renvoie l’objet correspondant.
  • JSON.stringify prend un objet supporté par JSON et renvoie la chaîne de caractères correspondante.

TP : Livre dont vous êtes le héros§

Sujet§

  • Récupérez cette archive, qui contient les différents chapitres d’un livre dont vous êtes le héros, sous forme de structures JSON.
  • Hébergez ces fichiers dans votre public_html, avec une application Javascript permettant de parcourir ce livre.

Indice

Pour les liens du livre, il vous est conseillé

  • d’utiliser des liens HTML internes (href="#xyz") qui n’entrainent pas de rechargement de la page, et
  • d’intercepter les changements en vous abonnant à l’événement hashchange de window, et en utilisant window.location pour déterminer le contenu à afficher.

Note

Une alternative consisterait à intercepter les clics sur les liens, pour savoir quand charger et afficher les éléments du livre. Cette solution peut sembler plus naturelle pour le programmeur, mais elle est crée une expérience utilisateur moins bonne :

  • la “navigation” à l’intérieur du livre n’est pas stockée dans l’historique du navigateur ;
  • par conséquent, le bouton “retour” ne fonctionne pas ;
  • un rechargement de la page redémarre au premier élément ;
  • il n’est pas possible de mettre un signet sur l’endroit où l’on se trouve.

Annexe§

fetch: le futur de XMLHttpRequest§

Ce standard est encore peu supporté par les navigateurs.

fetch('http://example.org').then(function(response) {
    if (response.ok) {
        return response.json()
    } else {
        throw ("Error " + response.status);
    }
}).then(function(data) {
    // do what you have to do with data
}).catch(function(err) {
    console.log(err);
});;