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 surgmail
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 serveursrv2
, 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 pasundefined
- Des structures complexes peuvent être représentées en JSON : tableaux d’objets, objets contenant d’autres objets...
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
dewindow
, et en utilisantwindow.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);
});;