Deuxième version : usage avancé

Parent Previous Next


Deuxième version : usage avancé



La deuxième version du XHR ajoute de nombreuses fonctionnalités intéressantes. Pour ceux qui se posent la question, le XHR2 ne fait pas partie de la spécification du HTML5. Cependant, cette deuxième version utilise de nombreuses technologies liées au HTML5, nous allons donc nous limiter à ce qui est utilisable (et intéressant) et nous verrons le reste plus tard, dans la partie consacrée au HTML5.

Tout d'abord, faisons une petite clarification :


Maintenant que tout est clair, entrons dans le vif du sujet : l'étude des nouvelles fonctionnalités.


Cependant, encore une petite chose : faites bien attention en utilisant cette deuxième mouture du XHR, peu de navigateurs sont encore capables de l'exploiter au maximum. Attendez-vous donc à des erreurs dues à de nombreuses incompatibilités. Toutefois, il est déjà possible de dire avec certitude que Firefox 6, Chrome 13 et Safari 5 sont suffisamment compatibles avec le XHR2 (bien que tout ne soit pas encore supporté).


Les requêtes cross-domain


Les requêtes cross-domain sont des requêtes effectuées depuis un nom de domaine A vers un nom de domaine B. Elles sont pratiques, mais absolument inutilisables avec la première version du XHR en raison de la présence d'une sécurité basée sur le principe de la same origin policy. Cette sécurité est appliquée aux différents langages utilisables dans un navigateur Web, le Javascript est donc concerné. Il est important de comprendre en quoi elle consiste et comment elle peut-être « contournée », car les requêtes cross-domain sont au cœur du XHR2.


Une sécurité bien restrictive


Bien que la same origin policy soit une sécurité contre de nombreuses failles, elle est un véritable frein pour le développement Web, car elle a pour principe de n'autoriser les requêtes XHR qu'entre les pages Web possédant le même nom de domaine. Si, par exemple, vous vous trouvez sur votre site personnel dont le nom de domaine est mon_site_perso.com et que vous tentez de faire une requête XHR vers le célèbre nom de domaine de chez Google google.com, vous allez alors rencontrer une erreur et la requête ne sera pas exécutée, car les deux noms de domaine sont différents.


Cette sécurité s'applique aussi dans d'autres cas, comme deux sous-domaines différents. Afin de vous présenter rapidement et facilement les différents cas concernés ou non par cette sécurité, voici un tableau largement réutilisé sur le Web.


Il illustre différents cas où les requêtes XHR sont possibles ou non. Les requêtes sont exécutées depuis la page http://www.example.com/dir/page.html :

URL appelée

Résultat

Raison

http://www.example.com/dir/page.html

Succès

Même protocole et même nom de domaine

http://www.example.com/dir2/other.html

Succès

Même protocole et même nom de domaine, seul le dossier diffère

http://www.example.com:81/dir/other.html

Échec

Même protocole et même nom de domaine, mais le port est différent (80 par défaut)

https://www.example.com/dir/other.html

Échec

Protocole différent (HTTPS au lieu de HTTP)

http://en.example.com/dir/other.html

Échec

Sous-domaine différent

http://example.com/dir/other.html

Échec

Si l'appel est fait depuis un nom de domaine dont les « www » sont spécifiés, alors il
faut faire de même pour la page appelée


Alors, certes, cette sécurité est impérative, mais il se peut que parfois nous possédions deux sites Web dont les noms de domaine soient différents, mais dont la connexion doit se faire par le biais des requêtes XHR. La deuxième version du XHR introduit donc un système simple et efficace permettant l'autorisation des requêtes cross-domain.


Autoriser les requêtes cross-domain


Il existe une solution implémentée dans la deuxième version du XHR, qui consiste à ajouter un simple en-tête dans la page appelée par la requête pour autoriser le cross-domain. Cet en-tête se nomme Access-Control-Allow-Origin et permet de spécifier un ou plusieurs domaines autorisés à accéder à la page par le biais d'une requête XHR.


Pour spécifier un nom de domaine, il suffit d'écrire :

Access-Control-Allow-Origin: http://example.com


Ainsi, le domaine http://example.com aura accès à la page qui retourne cet en-tête. Si vous souhaitez spécifier plusieurs noms de domaine, il vous faut alors utiliser le caractère | entre chaque nom de domaine :

Access-Control-Allow-Origin: http://example1.com | http://example2.com


Pour spécifier que tous les noms de domaine ont accès à votre page, utilisez l'astérisque * :

Access-Control-Allow-Origin: *


Il ne vous reste ensuite plus qu'à ajouter cet en-tête aux autres en-têtes de votre page Web, comme ici en PHP :

<?php

 

  header('Access-Control-Allow-Origin: *');

 

?>


Cependant, prenez garde à l'utilisation de cet astérisque, ne l'utilisez que si vous n'avez pas le choix, car, lorsque vous autorisez un nom de domaine à faire des requêtes cross-domain sur votre page, c'est comme si vous désactiviez une sécurité contre le piratage vis-à-vis de ce domaine.


Nouvelles propriétés et méthodes


Le XHR2 fournit de nombreuses propriétés supplémentaires ; quant aux méthodes, il n'y en a qu'une seule de nouvelle.


Éviter les requêtes trop longues


Il se peut que, de temps en temps, certaines requêtes soient excessivement longues. Afin d'éviter ce problème, il est parfaitement possible d'utiliser la méthode abort() couplée à setTimeout(), cependant le XHR2 fournit une solution bien plus simple à mettre en place. Il s'agit de la propriété timeout, qui prend pour valeur un temps en millisecondes. Une fois ce temps écoulé, la requête se terminera.

xhr.timeout = 10000; // La requête se terminera si elle n'a pas abouti au bout de 10 secondes


À l'heure où nous écrivons ces lignes, aucun navigateur ne supporte cette propriété. Essayez de bien vous renseigner sur son support avant de vous acharner à l'utiliser en vain.


Forcer le type de contenu


Vous souvenez-vous lorsque nous avions abordé le fait qu'il fallait bien spécifier le type MIME de vos documents afin d'éviter que vos fichiers XML ne soient pas parsés ? Eh bien, sachez que si vous n'avez pas la possibilité de le faire (par exemple, si vous n'avez pas accès au code de la page que vous appelez), vous pouvez réécrire le type MIME reçu afin de parser correctement le fichier. Cette astuce se réalise avec la nouvelle méthode overrideMimeType(), qui prend en paramètre un seul argument contenant le type MIME exigé :

var xhr = new XMLHttpRequest();

 

xhr.open('GET', 'http://example.com');

 

xhr.overrideMimeType('text/xml');

 

// L'envoi de la requête puis le traitement des données reçues peuvent se faire


Attention ! Cette méthode ne peut être utilisée que lorsque la propriété readyState possède les valeurs 1 ou 2. Autrement dit, lorsque la méthode open() vient d'être appelée ou bien lorsque les en-têtes viennent d'être reçus, ni avant, ni après.


Accéder aux cookies et aux sessions avec une requête cross-domain


Cela n'a pas été présenté plus tôt, mais il est effectivement possible pour une page appelée par le biais d'une requête XHR (versions 1 et 2) d'accéder aux cookies ou aux sessions du navigateur. Cela se fait sans contrainte, vous pouvez, par exemple, accéder aux cookies comme vous le faites d'habitude :

<?php

 

  echo $_COOKIE['cookie1']; // Aucun problème !

 

?>


Cependant, cette facilité d'utilisation est loin d'être présente lorsque vous souhaitez accéder à ces ressources avec une requête cross-domain, car aucune valeur ne sera retournée par les tableaux $_COOKIEet $_SESSION.


Pourquoi ? Les cookies et les sessions ne sont pas envoyés ?


Eh bien non ! Rassurez-vous, il ne s'agit pas d'une fonctionnalité conçue pour vous embêter, mais bien d'une sécurité, car vous allez devoir autoriser le navigateur et le serveur à gérer ces données.


Quand nous parlons du serveur, nous voulons surtout parler de la page appelée par la requête. Vous allez devoir y spécifier l'en-tête suivant pour autoriser l'envoi des cookies et des sessions :

Access-Control-Allow-Credentials: true


Mais, côté serveur, cela ne suffira pas si vous avez spécifié l'astérisque * pour l'en-tête Access-Control-Allow-Origin. Il vous faut absolument spécifier un seul nom de domaine, ce qui est malheureusement très contraignant dans certains cas d'applications (bien qu'ils soient rares).


Vous devriez maintenant avoir une page PHP commençant par un code de ce genre :

<?php

 

  header('Access-Control-Allow-Origin: http://example.com');

  header('Access-Control-Allow-Credentials: true');

 

?>


Cependant, vous pourrez toujours tenter d'accéder aux cookies ou aux sessions, vous obtiendrez en permanence des valeurs nulles. La raison est simple : le serveur est configuré pour permettre l'accès à ces données, mais le navigateur ne les envoie pas. Pour pallier ce problème, il suffit d'indiquer à notre requête que l'envoi de ces données est nécessaire. Cela se fait après initialisation de la requête et avant son envoi (autrement dit, entre l'utilisation des méthodes open() et send()) avec la propriété withCredentials :

xhr.open();

 

xhr.withCredentials = true; // Avec « true », l'envoi des cookies et des sessions est bien effectué

 

xhr.send();


Maintenant, une petite question technique pour vous : nous avons une page Web nommée client.php située sur un nom de domaine A. Depuis cette page, nous appelons la page server.php située sur le domaine Bgrâce à une requête cross-domain. Les cookies et les sessions reçus par la page server.php sont-ils ceux du domaine A ou bien ceux du domaine B ?


Bonne question, n'est-ce pas ? La réponse est simple et logique : il s'agit de ceux du domaine B. Si vous faites une requête cross-domain, les cookies et les sessions envoyés seront constamment ceux qui concernent le domaine de la page appelée. Cela s'applique aussi si vous utilisez la fonction PHP setcookie()dans la page appelée : les cookies modifiés seront ceux du domaine de cette page, et non pas ceux du domaine d'où provient la requête.


Une dernière précision, rappelez-vous bien que tout ce qui a été étudié ne vous concerne que lorsque vous faites une requête cross-domain ! Dans le cas d'une requête dite « classique », vous n'avez pas à faire ces manipulations, tout fonctionne sans cela, même pour une requête XHR1.


Quand les événements s'affolent


La première version du XHR ne comportait qu'un seul événement, la deuxième en comporte maintenant huit si on compte l'événement readystatechange ! Pourquoi tant d'ajouts ? Parce que le XHR1 ne permettait clairement pas de faire un suivi correct de l'état d'une requête.


Les événements classiques


Commençons par trois événements bien simples : loadstart, load et loadend. Le premier se déclenche lorsque la requête démarre (lorsque vous appelez la méthode send()). Les deux derniers se déclenchent lorsque la requête se termine, mais avec une petite différence : si la requête s'est correctement terminée (pas d'erreur 404 ou autre), alors load se déclenche, tandis que loadend se déclenche dans tous les cas. L'avantage de l'utilisation de load et loadend, c'est que vous pouvez alors vous affranchir de la vérification de l'état de la requête avec la propriété readyState, comme vous le feriez pour l'événementreadystatechange.


Les deux événements suivants sont error et abort. Le premier se déclenche en cas de non-aboutissement de la requête (quand readyState n'atteint même pas la valeur finale : 4), tandis que le deuxième s'exécutera en cas d'abandon de la requête avec la méthode abort() ou bien avec le bouton « Arrêt » de l'interface du navigateur Web.


Vous souvenez-vous de la propriété timeout ? Eh bien, sachez qu'il existe un événement du même nom qui se déclenche quand la durée maximale spécifiée dans la propriété associée est atteinte.


Le cas de l'événement progress


Pour finir, nous allons voir l'utilisation d'un événement un peu plus particulier nommé progress. Son rôle est de se déclencher à intervalles réguliers pendant le rapatriement du contenu exigé par votre requête. Bien entendu, son utilisation n'est nécessaire, au final, que dans les cas où le fichier rapatrié est assez volumineux. Cet événement a pour particularité de fournir un objet en paramètre à la fonction associée. Cet objet contient deux propriétés nommées loaded et total. Elles indiquent, respectivement, le nombre d'octets actuellement téléchargés et le nombre d'octets total à télécharger. Leur utilisation se fait de cette manière :

xhr.onprogress = function(e) {

 

    element.innerHTML = e.loaded +' / '+ e.total;

 

};


Au final, l'utilité de cet événement est assez quelconque, ce dernier a bien plus d'intérêt dans le cas d'un upload (mais cela sera abordé dans la partie consacrée au HTML5). Cela dit, il peut avoir son utilité dans le cas de préchargements de fichiers assez lourds. Ainsi, le préchargement de plusieurs images avec une barre de progression peut être une utilisation qui peut commencer à avoir son intérêt (mais, nous vous l'accordons, cela n'a rien de transcendant).


Cet événement n'étant pas très important, nous ne ferons pas un exercice expliqué pas à pas, toutefois, vous trouverez un lien vers un exemple en ligne dont le code est commenté, n'hésitez pas à y jeter un coup d’œil !


L'objet FormData


Cet objet consiste à faciliter l'envoi des données par le biais de la méthode POST des requêtes XHR. Comme nous l'avons dit plus tôt dans ce chapitre, l'envoi des données par le biais de POST est une chose assez fastidieuse, car il faut spécifier un en-tête dont on ne se souvient que très rarement de tête, on perd alors du temps à le chercher sur le Web.


Au-delà de son côté pratique en terme de rapidité d'utilisation, l'objet FormData est aussi un formidable outil permettant de faire un envoi de données binaires au serveur. Ce qui, concrètement, veut dire qu'il est possible de faire de l'upload de fichiers par le biais des requêtes XHR. Cependant, l'upload de fichiers nécessite des connaissances approfondies sur le HTML5, cela sera donc traité plus tard. Nous allons tout d'abord nous contenter d'une utilisation relativement simple.


Tout d'abord, l'objet FormData doit être instancié :

var form = new FormData();


Une fois instancié, vous pouvez vous servir de son unique méthode : append(). Celle-ci ne retourne aucune valeur et prend en paramètres deux arguments obligatoires : le nom d'un champ (qui correspond à l'attribut name des éléments d'un formulaire) et sa valeur. Son utilisation est donc très simple :

form.append('champ1', 'valeur1');

form.append('champ2', 'valeur2');


C'est là que cet objet est intéressant : pas besoin de spécifier un en-tête particulier pour dire que l'on envoie des données sous forme de formulaire. Il suffit juste de passer notre objet de type FormData à la méthodesend(), ce qui donne ceci sur un code complet :

var xhr = new XMLHttpRequest();

 

xhr.open('POST', 'http://example.com');

 

var form = new FormData();

form.append('champ1', 'valeur1');

form.append('champ2', 'valeur2');

 

xhr.send(form);


Et côté serveur, vous pouvez récupérer les données tout aussi simplement que d'habitude :

<?php

 

  echo $_POST['champ1'] . ' - ' . $_POST['champ2']; // Affiche : « valeur1 - valeur2 »

 

?>


Revenons rapidement sur le constructeur de cet objet, car celui-ci possède un argument bien pratique : passez donc en paramètre un élément de formulaire et votre objet FormData sera alors prérempli avec toutes les valeurs de votre formulaire. Voici un exemple simple :

<form id="myForm">

 

  <input id="myText" name="myText" type="text" value="Test ! Un, deux, un, deux !" />

 

</form>

 

<script>

 

  var xhr = new XMLHttpRequest();

 

  xhr.open('POST', 'http://example.com');

 

  var myForm = document.getElementById('myForm'),

      form = new FormData(myForm);

 

  xhr.send(form);

 

</script>


Ce qui, côté serveur, donne ceci :

<?php

 

  echo $_POST['myText']; // Affiche : « Test ! Un, deux, un, deux ! »

 

?>

Voilà tout, cet objet est, mine de rien, bien pratique, même si vous ne savez pas encore faire d'upload de fichiers. Il facilite quand même déjà bien les choses !


En résumé



Créé avec HelpNDoc Personal Edition: Créer des documents d'aide HTML facilement

Site à deux balles