Utiliser XMLHttpRequest
Baseline
Large disponibilité
*
Cette fonctionnalité est bien établie et fonctionne sur de nombreux appareils et versions de navigateurs. Elle est disponible sur tous les navigateurs depuis juillet 2015.
* Certaines parties de cette fonctionnalité peuvent bénéficier de prise en charge variables.
Dans ce guide, nous voyons comment utiliser XMLHttpRequest afin d'envoyer des requêtes HTTP pour échanger des données entre le site web et un serveur.
Des exemples d'utilisation courants et plus rares de XMLHttpRequest sont inclus.
Pour envoyer une requête HTTP :
- Créer un objet
XMLHttpRequest - Ouvrir une URL
- Envoyer la requête
Une fois la transaction terminée, l'objet XMLHttpRequest contient des informations utiles telles que le corps de la réponse et le statut HTTP du résultat.
function ecouteurRequete() {
console.log(this.responseText);
}
const req = new XMLHttpRequest();
req.addEventListener("load", ecouteurRequete);
req.open("GET", "http://www.example.org/example.txt");
req.send();
Types de requêtes
Une requête envoyée avec XMLHttpRequest peut récupérer les données de façon asynchrone ou de façon synchrone. Le comportement obtenu est choisi avec le troisième argument optionnel async de la méthode XMLHttpRequest.open(). Lorsque cet argument vaut true ou s'il n'est pas fourni, la requête est traitée de façon asynchrone. Sinon, le processus est géré de façon synchrone. Pour en savoir plus sur ces différents types de requêtes, vous pouvez consulter l'article Requêtes synchrones et asynchrones. Les requêtes synchrones ne peuvent pas être utilisées en dehors des workers, car elles bloquent l'interface principale.
Note :
Le constructeur XMLHttpRequest ne se limite pas aux seuls documents XML. Son nom commence par "XML", car lorsqu'il est créé, le format principal utilisé pour l'échange de données asynchrone est XML.
Gérer les réponses
Il existe plusieurs types d'attributs de réponse (angl.) définis pour le constructeur XMLHttpRequest(). Ces attributs indiquent au client qui a émis la requête des informations importantes quant au statut de la réponse. Pour les cas où il faut gérer une réponse qui n'est pas du texte, cela peut nécessiter des manipulations et une analyse que nous allons voir dans les sections suivantes.
Analyser et manipuler la propriété responseXML
Lorsqu'on utilise XMLHttpRequest pour obtenir le contenu d'un document XML distant, la propriété responseXML est un objet DOM qui contient le document XML analysé. La manipulation et l'analyse d'un tel résultat n'est pas nécessairement simple. Il existe quatre méthodes principales pour analyser un tel document XML :
- Utiliser XPath afin de cibler certains emplacements du document.
- Analyser et sérialiser manuellement le XML afin d'obtenir des chaînes de caractères ou des objets.
- Utiliser
XMLSerializerafin de sérialiser des arbres DOM en chaînes de caractères ou en fichiers. - Les expressions rationnelles
RegExpsont utilisées pour scanner le document si on ne connaît pas son contenu au préalable. On peut ainsi retirer les sauts de ligne par exemple. Attention, cette méthode n'est à utiliser qu'en dernier recours, car si le code XML change légèrement, il faut revoir la méthode.
Note :
XMLHttpRequest peut également interpréter un document HTML avec la propriété responseXML. Voir l'article à propos du HTML dans XMLHttpRequest pour apprendre comment faire.
Traiter une propriété responseText contenant un document HTML
Lorsqu'on utilise XMLHttpRequest afin d'obtenir le contenu d'une page HTML distante, la propriété responseText est une chaîne de caractères contenant le document HTML brut. La manipulation et l'analyse d'un tel résultat n'est pas nécessairement simple. Il existe trois méthodes principales pour analyser un tel document HTML :
- Utiliser la propriété
XMLHttpRequest.responseXMLcomme indiqué dans l'article HTML dansXMLHttpRequest. - Injecter le contenu dans le corps d'un fragment de document à l'aide de
fragment.body.innerHTMLet traverser le DOM de ce fragment. - Les expressions rationnelles
RegExpsont utilisées pour scanner le document si on ne connaît pas son contenu au préalable. On peut ainsi retirer les sauts de ligne par exemple. Attention, cette méthode n'est à utiliser qu'en dernier recours, car si le code HTML change légèrement, il faut revoir la méthode.
Gérer les données binaires
Bien que XMLHttpRequest soit le plus souvent utilisé pour envoyer et recevoir des données textuelles, il peut également être utilisé pour envoyer et recevoir du contenu binaire. Il existe plusieurs méthodes bien testées pour forcer la réponse d'un XMLHttpRequest à envoyer des données binaires. Celles-ci impliquent l'utilisation de la méthode overrideMimeType() sur l'objet XMLHttpRequest et constituent une solution viable.
const req = new XMLHttpRequest();
req.open("GET", url);
// Récupère les données non-traitées comme une chaîne de caractères binaire
req.overrideMimeType("text/plain; charset=x-user-defined");
/* … */
D'autres techniques plus modernes existent également. En effet responseType prend en charge plusieurs types de contenu, permettant ainsi d'envoyer et de recevoir des données binaires plus facilement.
Prenons le fragment de code qui suit, qui utilise responseType avec "arraybuffer" afin de récupérer le contenu distant dans un objet ArrayBuffer qui stocke les données binaires.
const req = new XMLHttpRequest();
req.onload = (e) => {
const arraybuffer = req.response; // pas responseText
/* … */
};
req.open("GET", url);
req.responseType = "arraybuffer";
req.send();
Pour plus d'exemples, voir la page Envoyer et recevoir des données binaires.
Connaître l'avancement
XMLHttpRequest fournit la possibilité d'écouter différents évènements qui peuvent se produire pendant le traitement de la requête. Cela inclut les notifications périodiques d'avancement, les notifications d'erreur, et ainsi de suite.
Prend en charge la surveillance des évènements progress pour les transferts XMLHttpRequest conformément à la spécification des évènements de progression (angl.) : ces évènements implémentent l'interface ProgressEvent. Les évènements réels que vous pouvez surveiller pour déterminer l'état d'un transfert en cours sont :
progress-
Le nombre de données qui ont été récupérées a changé.
load-
Le transfert est terminé ; toutes les données sont désormais dans
response
const req = new XMLHttpRequest();
req.addEventListener("progress", mettreAJourProgress);
req.addEventListener("load", transfertTermine);
req.addEventListener("error", transfertEchoue);
req.addEventListener("abort", transfertAnnule);
req.open();
// …
// Avancement du transfert du serveur au client (téléchargements)
function mettreAJourProgress(event) {
if (event.lengthComputable) {
const percentComplete = (event.loaded / event.total) * 100;
// …
} else {
// Impossible de connaître l'avancement, car la taille
// totale est inconnue
}
}
function transferComplet(evt) {
console.log("Le transfert est terminé.");
}
function transferEchoue(evt) {
console.log("Une erreur est survenue lors du transfert du fichier.");
}
function transferAnnule(evt) {
console.log("Le transfert a été annulé.");
}
Les lignes 3 à 6 du fragment ci-avant ajoutent les gestionnaires d'évènements pour les différents évènements émis à propos du transfert des données à l'aide de XMLHttpRequest.
Note :
Ces gestionnaires d'évènements doivent être ajoutés avant d'appeler open() sur la requête. Sinon, les évènements progress ne sont pas captés.
Le gestionnaire d'évènement pour l'avancement, porté par la fonction mettreAJourProgress() dans l'exemple, reçoit le nombre total d'octets à transférer (total) ainsi que le nombre d'octets transférés jusqu'à présent (loaded). Toutefois, si le champ lengthComputable vaut false, la longueur totale est inconnue et vaut 0 par défaut.
Les évènements d'avancement existent pour les téléchargements (downloads en anglais) et les téléversements (uploads en anglais). Pour les téléchargements, les évènements sont déclenchés sur l'objet XMLHttpRequest, comme illustré dans l'exemple précédent. Pour les téléversements, les évènements sont déclenchés sur l'objet XMLHttpRequest.upload, comme ceci :
const req = new XMLHttpRequest();
req.upload.addEventListener("progress", mettreAJourProgress);
req.upload.addEventListener("load", transfertComplet);
req.upload.addEventListener("error", transfertEchoue);
req.upload.addEventListener("abort", transfertAnnule);
req.open();
Note :
Les évènements d'avancement ne sont pas disponibles pour le protocole file:.
Les évènements d'avancement sont émis à chaque fragment (chunk) de données reçu, y compris le dernier fragment pour les cas où le paquet est reçu et la connexion fermée avant que l'évènement soit déclenché. Dans ce cas, l'évènement d'avancement est automatiquement déclenché lorsque l'évènement de chargement se produit pour ce paquet. Cela permet de surveiller l'avancement de façon fiable, à l'aide du seul évènement « progress ».
On peut également détecter les trois conditions de fin de chargement (abort, load ou error) à l'aide de l'évènement loadend :
req.addEventListener("loadend", finChargement);
function finChargement(e) {
console.log(
"Le transfert est terminé (mais on ne sait pas s'il a réussi ou non).",
);
}
Notez qu'il n'est pas possible de savoir, à partir des informations reçues par l'évènement loadend, quelle condition a provoqué la fin de l'opération ; toutefois, vous pouvez utiliser cet évènement pour gérer les tâches qui doivent être effectuées dans tous les scénarios de fin de transfert.
Obtenir la date de dernière modification
function obtenirEnTeteTemps() {
console.log(this.getResponseHeader("Last-Modified")); // Une date GMTString valide ou null
}
const req = new XMLHttpRequest();
req.open(
"HEAD", // On utilise HEAD, car on ne veut récupérer que les en-têtes
"votrepage.html",
);
req.onload = obtenirEnTeteTemps;
req.send();
Réaliser une action lorsque la date de dernière modification change
Créons deux fonctions :
function obtenirEnTeteTemps() {
const derniereVisite = parseFloat(
window.localStorage.getItem(`lm_${this.filepath}`),
);
const derniereModification = Date.parse(
this.getResponseHeader("Last-Modified"),
);
if (isNaN(derniereVisite) || derniereModification > derniereVisite) {
window.localStorage.setItem(`lm_${this.filepath}`, Date.now());
isFinite(derniereVisite) &&
this.callback(derniereModification, derniereVisite);
}
}
function siAChange(URL, fonctionRappel) {
const req = new XMLHttpRequest();
req.open(
"HEAD" /* On utilise HEAD, car on ne veut récupérer que les en-têtes */,
URL,
);
req.callback = fonctionRappel;
req.filepath = URL;
req.onload = obtenirEnTeteTemps;
req.send();
}
Pour tester cet exemple :
// Testons le fichier "votrepage.html"
siAChange("votrepage.html", function (modifie, visite) {
console.log(
`La page '${this.filepath}' a été modifiée le ${new Date(
modifie,
).toLocaleString()} !`,
);
});
Si vous souhaitez savoir si la page actuelle a changé, voyez l'article document.lastModified.
XMLHttpRequest inter-site
Les navigateurs modernes prennent en charge les requêtes inter-sites en implémentant le standard de partage de ressource entre les origines (CORS). Tant que le serveur est configuré pour autoriser les requêtes depuis l'origine de votre application web, XMLHttpRequest fonctionne correctement. Dans le cas contraire, une exception INVALID_ACCESS_ERR est levée.
Outrepasser le cache
Pour outrepasser le cache avec une méthode qui fonctionne dans les différents navigateurs, on peut ajouter un horodatage à l'URL en s'assurant d'encoder correctement la valeur (avec ? ou & où c'est nécessaire). Ainsi :
http://example.com/truc.html -> http://example.com/truc.html?12345 http://example.com/truc.html?bidule=machin -> http://example.com/truc.html?bidule=machin&12345
Le cache local étant indexé avec les URL, chaque requête est ainsi unique et passe outre le cache.
On peut ajuster les URL automatiquement avec le code qui suit :
const req = new XMLHttpRequest();
req.open("GET", url + (/\?/.test(url) ? "&" : "?") + new Date().getTime());
req.send(null);
Sécurité
La méthode recommandée pour activer les scripts inter-sites consiste à utiliser l'en-tête HTTP Access-Control-Allow-Origin dans la réponse à la requête XMLHttpRequest.
Interruptions des requêtes XHR
Si vous constatez qu'une requête XMLHttpRequest reçoit status=0 et statusText=null, cela signifie que la requête n'a pas été autorisée. Son état est UNSENT (angl.). Cela se produit probablement lorsque l'origine de XMLHttpRequest (au moment de sa création) a changé lorsque la méthode open() est appelée par la suite. Ce cas peut se produire, par exemple, lorsqu'une requête XMLHttpRequest est déclenchée par un évènement de déchargement sur une fenêtre, que la requête XMLHttpRequest attendue est créée alors que la fenêtre à fermer est encore présente, puis que la requête est envoyée (autrement dit, que open() est appelée) lorsque cette fenêtre n'est plus sélectionnée et qu'une autre fenêtre l'est. Le moyen le plus efficace d'éviter ce problème consiste à ajouter un écouteur à l'évènement DOMActivate de la nouvelle fenêtre, qui se déclenche lorsque l'évènement unload de la fenêtre fermée se déclenche.
Spécifications
| Spécification |
|---|
| XMLHttpRequest> # interface-xmlhttprequest> |