Logo JavaScript

Chapitre 8: Échanges de données avec AJAX

Objectifs:

  • Interroger une API du Web en JavaScript
  • Envoyer des informations à une API du Web

Plan du chapitre:

  1. Effectuer une requête GET avec XMLHttpRequest (Slides du TP)
    • Exercice: afficher la météo de la ville de l'utilisateur
  2. Effectuer une requête POST avec XMLHttpRequest (Slides du TP)
    • Exercice: tweeter un message

Effectuer une requête GET avec XMLHttpRequest

Introduction

Créé en 1995, le procédé AJAX (Asynchronous Javascript and XML) a été intégré aux navigateurs pour permettre aux sites Web d'être plus dynamiques.

Cette technique permet à une application JavaScript de:

  • échanger des données avec des serveurs Web;
  • charger des données sur une page sans la recharger;
  • accéder à des API (Application Programming Interface) du Web.

Une requête AJAX consiste à effectuer une requête HTTP depuis du code JavaScript.

Pour rappel, HTTP est le protocole employé par les navigateurs pour communiquer avec les serveurs Web.

Par exemple, quand nous allons sur Google, nous tapons http://google.com dans la barre du navigateur, ce qui a pour effet de demander la page HTML de recherche au serveur HTTP de google.com. Ceci est une requête de type GET, car elle permet seulement de récupérer des données depuis ce serveur, contrairement aux requêtes POST permettant d'envoyer des données à un serveur.

A noter que le chargement d'une page HTML déclanche généralement d'autres requêtes HTTP, afin de récupérer toutes les ressources associées à cette page Web: scripts, feuilles de style CSS, images, etc...

diagramme séquence ajax requête HTTP GET google

Formats de données

Une requête HTTP permet de récupérer des données de formats variés.

Par exemple:

  • le format HTML, pour les pages Web,
  • le format CSS, pour les feuilles de styles,
  • le format JS, pour les scripts en JavaScript,
  • le format JPG, pour les images, etc...

Nous allons nous intéresser en particulier aux formats représentant des données structurées.

Initialement, AJAX a été créé pour récupérer des données au format XML. Un format proche du HTML, et très en vogue à l'époque.

Exemple de document XML:

<inbox>
  <email from="amazon">votre colis a été envoyé</email>
</inbox>

Comme en HTML, un document XML est composé d'éléments, d'attributs, et de noeuds textuels.

Désormais, la majorité des sites Web emploient le format JSON pour échanger des données structurées.

Exemple de document JSON:

[
  {
    "from": "amazon",
    "subject": "votre colis a été envoyé"
  }
]

Pour rappel, JSON (JavaScript Object Notation) est en fait la syntaxe utilisé pour définir des objets en JavaScript.

En supposant que cet objet JSON soit stocké dans une variable objet, le langage JavaScript nous permet d'accéder facilement aux données qui y sont stockées.

Par exemple, pour afficher la valeur de la propriété subject de la propriété email, il suffit de saisir:

console.log(objet.email.subject);

Émettre une requête HTTP GET avec AJAX

Pour récupérer des données depuis une URL HTTP, le navigateur met à notre disposition la classe XMLHttpRequest. Malgré son nom, il est possible de l'utiliser pour récupérer des données exprimées dans d'autres formats que XML.

Voici un exemple de requête HTTP GET simple, en JavaScript:

var xhr = new XMLHttpRequest(); 
xhr.open('GET', 'https://jsonplaceholder.typicode.com/users/1');
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4) {
    alert(xhr.responseText);
  }
};
xhr.send();

Cette requête permet de récupérer le texte d'un document disponible à l'URL https://jsonplaceholder.typicode.com/users/1, puis de l'afficher dans un alert() une fois qu'il a été téléchargé intégralement.

Effectuer une requête avec XMLHttpRequest consiste en 4 étapes:

  1. instancier la classe, avec new;
  2. spécifier la méthode (GET) et l'URL du document à récupérer, en appelant la méthode open();
  3. définir ce qu'on souhaite faire de la réponse, une fois reçue intégralement, en affectant une fonction à la propriété onreadystatechange de notre instance;
  4. puis envoyer la requête, en appelant la méthode send() de notre instance.

La propriété readyState permet de savoir où en est le téléchargement du document. Lorsque celui-ci est complet, readyState vaut 4, et la fonction affectée à onreadystatechange est appelée à nouveau par le navigateur. À ce moment là, nous pouvons accéder au contenu du document téléchargé, stocké dans la propriété responseText.

À noter que, même si le document en question est au format JSON (par exemple), responseText contiendra la version sérialisée du document, c'est à dire qu'il sera de type string, et non object. À ce stade, il n'est donc pas possible d'accéder à une propriété spécifique de cet objet.

Nous allons voir comment convertir cette chaîne de caractères en veritable objet dans la prochaine partie.

Récupérer un document JSON avec AJAX

Dans le cas où notre requête retourne un document JSON, il est possible de convertir sa représentation textuelle (fournie dans la propriété responseText) en véritable objet JavaScript.

Pour cela, le navigateur fournit la fonction JSON.parse().

Cette fonction prend en paramètre une chaîne de caractères (type: string) contenant la version sérialisée d'un objet, et retourne l'objet correspondant (type: object).

Par exemple, imaginons l'objet sérialisé suivant:

var chaine = '{"message":"bonjour!"}';
console.log(chaine.message); // => undefined

Comme cet objet est sérialisé sous forme de chaine de caractères, il n'est pas possible d'accéder directement à ses propriétés.

Pour permettre cela, nous le passer en paramètre de la fonction JSON.parse():

var chaine = '{"message":"bonjour!"}';
var objet = JSON.parse(chaine);
console.log(objet.message); // => "bonjour!"

Donc, pour récupérer un objet JavaScript depuis la réponse JSON de notre requête AJAX, il faut utiliser le code suivant:

var xhr = new XMLHttpRequest(); 
xhr.open('GET', 'https://jsonplaceholder.typicode.com/users/1');
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4) {
    var reponse = JSON.parse(xhr.responseText);
    alert(reponse.name);
  }
};
xhr.send();

Bien entendu, n'oubliez pas de remplacer l'URL de cet exemple par celle à laquelle se trouve le document que vous souhaitez récupérer !

N'oubliez pas non plus de consulter la console de votre navigateur ainsi que la partie "Réseau" (ou "Network", en anglais) de Chrome Dev Tools, afin de diagnostiquer les éventuelles erreurs.

Exemples d'API interrogeables via AJAX

APIs simples:

APIs plus complexes:

Annuaire d'APIs: Programmable Web

Note: Certaines APIs nécessitent l'utilisation d'une clé API. Donc:

  • lisez la documentation de l'API,
  • créez un compte développeur sur le site de l'API, si besoin,
  • lisez les parties suivantes pour intégrer l'éventuelle clé API dans l'URL de votre requête AJAX.

Exercice 1: Récupérer l'adresse IP de l'utilisateur

Effectuer une requête AJAX vers l'API httpbin.org/ip qui affichera seulement l'heure fournie en réponse dans un alert().

Solution: ajax-get-ip.js.

Exercice 2: Météo de la ville de l'utilisateur

Réaliser une page Web permettant d'afficher la météo d'une ville saisie par l'utilisateur. Pour récupérer la météo, utiliser une requête AJAX vers l'API de openweathermap.

Etapes:

  1. réaliser une page HTML contenant un champ de saisie et un bouton.
  2. ajouter un code JavaScript qui affiche le contenu du champ quand on clique sur le bouton.
  3. ajouter la requête AJAX dans le code, afficher le résultat dans la console.
  4. faire en sorte que l'appel AJAX s'adapte en fonction de la saisie, à chaque clic sur le bouton.

Solution: ajax-get-weather.html.

Exercice 3: Afficher les images demandées

Réaliser une page Web permettant d'afficher des images ou animations en rapport avec les mots-clés saisis par l'utilisateur. Pour récupérer l'URL des images, utiliser une requête AJAX vers l'API de Flickr ou de Giphy.

Les images doivent être affichées directement dans la page à l'aide d'éléments <img>, après la saisie de l'utilisateur. Pour cela, vous pouvez utiliser innerHTML, ou les méthodes présentées dans le chapitre sur la manipulation avancée du DOM.

Solution: flickr, giphy

Restrictions de protocole HTTP / HTTPS

Comme vous le savez probablement, le protocole HTTP existe aussi en version sécurisée (HTTPS).

Pour des raisons de sécurité, les navigateurs empêchent le protocole HTTP d'être employé depuis une page Web affichée via HTTPS. Cette contrainte a des implications importantes sur le fonctionnement des requêtes AJAX.

Notamment:

  • Une page ouverte en HTTPS (ex: jsfiddle) ne peut pas effectuer de requête AJAX vers des URLs HTTP (non sécurisée), seulement vers des URLs HTTPS.
  • Si vous exécutez une requête AJAX depuis la console d'une page HTTPS, les mêmes constraintes s'appliquent.

Donc, dans la mesure du possible, assurez-vous d'utiliser le protocole HTTPS dans l'URL de vos requêtes AJAX.

Restrictions de domaines

En plus des restrictions de sécurité HTTPS, certaines APIs ne permettent pas l'exécution de requêtes AJAX depuis certains domaines. (ex: jsfiddle)

D'autres refusent carrément de répondre aux requêtes émises depuis d'autres domaines que le leur.

Dans ce cas, vous pourrez seulement exécuter des requêtes AJAX depuis une page de leur site Web, via la console.

Manipuler les paramètres d'une URL HTTP

Dans les exemples d'APIs "complexes" fournis plus haut, vous remarquerez que certaines URLs contiennent des paramètres, séparés par les caractères ? puis &.

Exemple:

https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=74a9b070d21072ccac3a7b5f44f09efa&tags=soccer&format=json&nojsoncallback=1

Comme dans la définition de propriétés d'un objet JavaScript, chaque paramètre est composé d'un nom (aussi appelé clé) et d'une valeur. Dans une URL, le nom et la valeur de chaque paramètre sont séparés par un =.

Dans l'URL fournie ci-dessus en exemple, quatre paramètres sont transmis:

  • le paramètre nommé method vaut flickr.photos.search,
  • le paramètre nommé api_key vaut 74a9b070d21072ccac3a7b5f44f09efa,
  • le paramètre nommé tags vaut soccer,
  • le paramètre nommé format vaut json,
  • et le paramètre nommé nojsoncallback vaut 1.

De le cas où vous voudriez passer la valeur d'une variable de votre programme JavaScript en paramètre de votre URL, vous pouvez utiliser la concaténation.

Exemple:

var tags = 'soccer';
var url = 'https://api.com/?tags=' + tags + '&format=json';

Attention: Ceci fonctionne bien quand la valeur de votre variable ne contient aucun caractère spécial. Par contre que se passerait-il si elle contenait un espace, ou un & ? L'URL deviendrait alors invalide...

Pour éviter que les caractères spéciaux éventuels d'une chaine de caractères ne puisse invalider votre URL, utilisez la fonction encodeURIComponent() de la manière suivante:

var tags = 'soccer';
var url = 'https://api.com/?tags=' + encodeURIComponent(tags) + '&format=json';

Identification du développeur et/ou de l'application

Vous aurez remarqué que certaines URLs contiennent un paramètre api_key, ou appid.

Exemple:

http://api.openweathermap.org/data/2.5/weather?q=London,uk&appid=44db6a862fba0b067b1930da0d769e98

Ce genre de paramètre permet d'identifier l'application utilisant l'API, ou le développeur de cette application. En effet, les fournisseurs d'APIs souhaitent connaitre cette information, afin de contrôler l'accès et l'usage de leur API.

Pour obtenir une clé, rendez-vous sur le site Web de cette API, puis intégrez la clé fournie dans l'URL de votre/vos requête(s) AJAX.


Effectuer une requête POST avec XMLHttpRequest

Dans la partie précédente, nous avons utilisé la classe XMLHttpRequest pour envoyer des requêtes de type HTTP GET. Pour rappel, ce type de requête permet de récupérer des informations depuis un serveur.

Dans cette partie, nous allons voir comment utiliser cette même classe pour envoyer des requêtes de type HTTP POST. Même s'il permet aussi de recevoir une réponse du serveur à ces requêtes, ce type de requête permet d'envoyer des informations.

Envoi d'une chaine de caractères

Voici un exemple de requête POST effectuée en JavaScript/AJAX:

var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://httpbin.org/post');
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4) {
    alert(xhr.responseText);
  }
};
// envoi d'une chaine de caractères:
xhr.send('ceci est un exemple de données envoyées');

Le principe de fonctionnement est exactement le même que celui d'une requête GET, sauf que nous avons cette fois-ci envoyé une chaine de caractères via notre requête à l'adresse serveur https://httpbin.org/post.

Pour cela, nous avons:

  • remplacé le paramètre GET par POST, dans l'appel à la méthode open(),
  • et passé une chaine de caractères en paramètre de l'appel à la méthode send().

Envoi d'un objet JavaScript / JSON

Pour envoyer un objet JavaScript / JSON dans une requête POST, il faut d'abord sérialiser l'objet (c'est à dire: le convertir) sous forme de chaine de caractères, à l'aide de la fonction JSON.stringify().

Il suffit donc de modifier le paramètre passé à la méthode send(), tel que dans l'exemple suivant:

// ... ou envoi d'un objet JSON:
xhr.send(JSON.stringify({ message: 'bonjour!' }));

En effet, JSON.stringify() est la fonction inverse de JSON.parse():

  • alors que JSON.parse() permet de convertir une chaine de caractères en objet,
  • JSON.stringify() permet de convertir un objet en chaine de caractères.

Attention: veillez à ne pas appeler la méthode send() d'une même instance (ex: xhr, dans notre exemple) plus d'une seule fois !

Conseils pratiques pour diagnostiquer le fonctionnement de vos requêtes

  • Comme d'habitude, n'oubliez pas de consulter la console de votre navigateur pour vérifier la présence éventuelle d'erreurs dans votre code et/ou dans vos requêtes AJAX. (ex: accès non autorisé à une API)
  • Utilisez l'onglet "Réseau" (ou Network, en anglais) de Chrome Dev Tools pour suivre l'exécution de vos requêtes, et consulter à la fois leur contenu et celui de la réponse du serveur.
  • Utilisez l'API https://httpbin.org/post pour tester le bon fonctionnement de vos requêtes POST. Cette API vous envoie en réponse le contenu que le serveur a reçu suite à votre requête.

Exercice 4: Tweeter en AJAX

Un serveur est mis à votre disposition à l'URL https://js-ajax-twitter.herokuapp.com. Comme Twitter, l'application Web exécutée sur ce serveur permet aux utilisateurs de publier des messages publics en temps réél.

Le but de cet exercice est de publier vos messages en utilisant une requête AJAX.

Sont fournis:

  • une page web affichant en temps réél le flux des derniers messages publiés sur le serveur;
  • une API HTTP permettant de publier des messages sur le serveur, accessible depuis l'adresse /tweet;
  • et un client simple permettant de publier des messages à l'aide d'un formulaire HTML.

Documentation de l'API fournie

Pour publier un message sur ce serveur, il faut envoyer une requête HTTP POST à l'adresse /tweet (accessible depuis la racine du serveur), en transmettant un objet JSON.

L'objet JSON à envoyer comme contenu de la requête doit contenir deux propriétés:

  • message: le texte à publier. (type: string)
  • token: le jeton fourni après identification de l'utilisateur. (type: string)

Note: La valeur de la propriété token de l'objet à envoyer est générée automatiquement par le client simple fourni, dans la variable globale window.token.

À chaque requête HTTP valide reçue, le serveur répondra par un objet JSON ayant:

  • soit une propriété error contenant un message d'erreur (type: string);
  • soit une propriété ok, dans le cas où le message a été publié sans erreur.

Remarque importante: le bouton d'identification de Google n'a été activé que depuis les domaines jsbin.com et js-ajax-twitter.herokuapp.com. Vous ne pourrez donc pas exécuter le client simple fourni (ni une version dérivée) depuis un autre domaine, ni depuis le système de fichiers de votre machine (protocole file://).

Étapes proposées

  1. Tester le client fourni: https://js-ajax-twitter.herokuapp.com/client.html (après login)
  2. Cloner le client fourni: https://jsbin.com/bucilir/edit?html,js,output
  3. Remplacer le formulaire par l'envoi d'une requête HTTP POST à chaque fois que l'utilisateur pressera ENTRÉE dans le champ de saisie.
  4. Dans le cas où la publication du message a fonctionné sans erreur, effacer le champ de saisie, afin de permettre la saisie immédiate d'un nouveau message.
  5. Dans le cas contraire, afficher une description de l'erreur dans un alert().

Conseil: Pensez à utiliser la console JavaScript et l'onglet "Réseau" de Chrome Dev Tools pour diagnostiquer.

Bonus: améliorations

Quand vous aurez terminé toutes les étapes proposées ci-dessus, vous pourrez apporter les améliorations suivantes à votre client:

  • intégrer le flux de tous les messages sur votre page,
  • améliorer le design et l'expérience utilisateur de votre client.

Solution: jsbin

results matching ""

    No results matching ""