Attention : Le contenu de ces pages n'a pas été mis à jour depuis longtemps. Il est probablement obsolète pour Firefox 4.0/Gecko 4.0 et supérieur. Pour du contenu plus récent, allez consulter developer.mozilla.org.

Request

Introduction

Il est possible d'effectuer des requêtes HTTP vers un serveur web en lui passant des paramètres, que ce soit par la méthode GET ou par la méthode POST. On peut simuler en javascript ce que l'on peut faire avec un formulaire HTML (balise « form » avec l'attribut « method » à "GET" ou "POST").

Voir le détail sur les ApplisWeb/MethodesRequetesHttp

Objet XMLHttpRequest

Cet objet en Javascript sous Mozilla nous permet d'envoyer une requête HTTP. Il dispose de plusieurs méthodes et propriétés pour contrôler l'envoi de la requête et la récupération que l'on reçoit en retour.

Méthodes

open
ouverture de la connection au serveur
; setRequestHeader: pour spécifier les entêtes de la requête
send
pour envoyer le corps de la requète.

Propriétés

onload
contient la référence à la fonction à executer une fois la réponse reçue ;
status
indique le code retour (protocole HTTP) de la requête (404, 200, etc.) ;
; responseText: réponse sous la forme d'une chaîne de caractères ;
responseXML
réponse sous la forme d'un arbre XML.

Pour les détails de cet objet, voir Reference/Javascript/XmlHttpRequest.

Principes

Pour un envoi simple d'une requête HTTP en synchrone (c'est-à-dire que le script Javascript attende de recevoir la réponse avant de continuer), voici le principe :

  1. instanciation d'un objet XMLHttpRequest ;
  2. paramétrage de certaines propriétés, comme onload ;
  3. ouverture de la connection avec open ;
  4. spécification des en-têtes HTTP ;
  5. envoi du contenu ;
  6. test du code retour et éventuellement, récupération du contenu reçu.

Voyons ce que cela donne avec les différentes méthodes HTTP.

Méthode « GET »

Notre script ressemblera à ceci :

  p = new XMLHttpRequest();
  p.onload = null;
  p.open("GET", "http://monsite.tld/mon_fichier.xml", false);
  p.send(null);
  if ( p.status != "200" )
    {
      alert("Réception erreur " + p.status);
    }
  else
    {
      contenu = p.responseText;
      // traitement du contenu
    }

Ici, nous interrogeons le serveur Web, en lui demandant l'url « http://monsite.tld/mon_fichier.xml » qui correspondant à un fichier XML. Comme c'est une méthode GET, il n'y a pas d'entête à spécifier (donc pas d'appel à setRequestHeader). Après envoi, on récupère le code de retour. Si il vaut 200, c'est que tout s'est bien passé. On peut alors récupérer le contenu de la réponse (donc ici, le contenu du fichier « mon_fichier.xml ») et ensuite le traiter (en faisant appel au DOM par exemple).

Remarquez le premier paramètre à open indiquant le type de la méthode : ici "GET".

À noter

On pourrait aussi faire :

  p.open("GET", "http://monsite.com/mon_script.php?param1=val1&param2=val2", false);

Et mon_script.php renverrait un contenu HTML ou XML. Il peut récupérer les paramètres dans le tableau PHP $_GET.

Méthode « POST »

Quelques différences avec la méthode « GET » :

  p = new XMLHttpRequest();
  p.onload = null;
  p.open("POST", "http://monsite.tld/mon_script.php", false);
  p.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  var corps = "param1=val1&param2=val2";
  p.send(corps);
  if ( p.status != "200" )
    {
      alert("Réception erreur " + p.status);
    }
  else
    {
      contenu = p.responseText;
      // traitement du contenu
    }

Ici donc, à l'appel de la méthode open, on indique la méthode HTTP « POST ». On spécifie le type MIME du contenu du corps de la requête qui sera de simples valeurs encodées : application/x-www-form-urlencoded.

Ensuite on construit le corps de la requète (variable corps). Remarque : il faut encoder les valeurs et les noms des paramètres, grâce à la fonction escape(), pour « échapper » certains caractères.

On envoie ensuite le corps de la requête avec send. Et enfin, on traite la réponse comme dans GET. « » Dans l'exemple, on appelle un script PHP. Celui-ci peut récupérer les paramètres dans le tableau PHP $_POST.

La méthode POST avec envoi de fichier

Là, ça devient du sport et il est important de bien respecter le protocole HTTP. Dans l'exemple qui suit, nous allons émuler le formulaire suivant :

 <form enctype="multipart/form-data" action="page.php" method="post">
   <input type="hidden" name="addfile" value="1">
   <input type="file" name="filename" />
   <input type="submit" value="Add" />
 </form>

Ce formulaire est composé d'un champ de téléversement de fichier et d'un paramètre de type hidden (caché). Reportez-vous à la page ApplisWeb/MethodesRequetesHttp pour connaitre la requête exacte que l'on va devoir envoyer.

Petites remarques préalables

La méthode send accepte plusieurs types de paramètres qui sont :

  • une chaine de caractères (comme utilisé dans l'exemple précédent) ;
  • un « DomDocument » (j'imagine qu'il sagit d'un document XML) ;
  • un « InputStream » qui est celui qui nous interesse.

Le but du jeu va donc d'être de créer notre requête en mettant tout dans un « InputStream ».

Marche à suivre

  1. Créer un « MultiplexInputStream » qui est un flux composé d'autre flux (un conteneur de flux en quelque sorte).
  2. Récupérer un flux de fichier (« FileInputStream ») et le mettre dans un flux bufferisé (« BufferedInputStream »). J'ignore pourquoi il faut le mettre dans un « BufferedInputStream » mais c'est le seul que je suis arrivé à faire fonctionner avec un fichier de type binaire. « BinaryInputStream » ne fonctionne pas et « StringInputStream » ne permet que d'envoyer des fichiers de type texte.
  3. Créer un flux de type chaine de caractères (« StringInputStream ») dans lequel nous allons mettre les entêtes spécifiques à chaque partie du message et le paramètre de type hidden.
  4. Créer un autre flux de type chaine de caractères qui va contenir la fin de la requête.
  5. Ajouter les 3 flux dans le « MultiplexInputStream ».
  6. Préparer la requête avec les entêtes nécessaires.
  7. Prier.
  8. Envoyer la requête.

Source

 /*
  * Je pars du principe que vous savez créer des objets XPCOM en Javascript
  * et que vous avez déja un objet de type « File ».
  * Une façon simple d'en obtenir un est d'utiliser l'objet « FilePicker »
  * (boite de dialogue de séléction de fichier) comme suit :
  */
 var nsIFilePicker = Components.interfaces.nsIFilePicker;
 var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
 fp.init(window, "Sélectionnez un fichier", nsIFilePicker.modeOpen);
 var res = fp.show();
 dump("Le fichier " + fp.file.leafName + " a été selectionné et pèse " + fp.file.fileSize + "\n");
 const BOUNDARY = "111222111"; //ce qui va nous servir de délimiteur
 const MULTI    = "@mozilla.org/io/multiplex-input-stream;1";
 const FINPUT   = "@mozilla.org/network/file-input-stream;1";
 const STRINGIS = "@mozilla.org/io/string-input-stream;1";
 const BUFFERED = "@mozilla.org/network/buffered-input-stream;1";
 const nsIMultiplexInputStream = Components.interfaces.nsIMultiplexInputStream;
 const nsIFileInputStream      = Components.interfaces.nsIFileInputStream;
 const nsIStringInputStream    = Components.interfaces.nsIStringInputStream;
 const nsIBufferedInputStream  = Components.interfaces.nsIBufferedInputStream;
 /* 1 */
 var mis = Components.classes[MULTI].createInstance(nsIMultiplexInputStream);
 /* 2 */
 var fin = Components.classes[FINPUT].createInstance(nsIFileInputStream);
 fin.init(fp.file, 0x01, 0444, null); //fic est un objet de type fichier
 var buf = Components.classes[BUFFERED].createInstance(nsIBufferedInputStream);
 buf.init(fin, 4096);
 /* 3 */
 var hsis = Components.classes[STRINGIS].createInstance(nsIStringInputStream);
 var sheader = new String();
 sheader += "--" + BOUNDARY + "\r\nContent-disposition: form-data;name=\"addfile\"\r\n\r\n1";
 sheader += "\r\n" + "--" + BOUNDARY + "\r\n"
 sheader += "Content-disposition: form-data;name=\"filename\";filename=\"" + fp.file.leafName + "\"\r\n";
 sheader += "Content-Type: application/octet-stream\r\n";
 sheader += "Content-Length: " + fp.file.fileSize+"\r\n\r\n";
 hsis.setData(sheader, sheader.length);
 /* 4 */
 var endsis = Components.classes[STRINGIS].createInstance(nsIStringInputStream);
 var bs = new String("\r\n--" + BOUNDARY + "--\r\n");
 endsis.setData(bs, bs.length);
 /* 5 */
 mis.appendStream(hsis);
 mis.appendStream(buf);
 mis.appendStream(endsis);
 /* 6 */
 var xmlr = new XMLHttpRequest();
 xmlr.open("POST", "http://...", false);  //À faire : remplacer par l'URL correcte
 xmlr.setRequestHeader("Content-Length", mis.available());
 xmlr.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
 /* 7 */
 // ^_^
 /* 8 */
 xmlr.send(mis);

En théorie (et en pratique chez moi), cela devrait fonctionner et le fichier devrait être envoyé. Il ne reste plus qu'à recupérer le contenu dans le script appelé. En PHP, le tableau $_POST contiendra « addfile=1 » et le tableau $_FILES les informations sur le fichier que nous avons envoyé.

Traitement des données par le serveur

Avec « XMLHttpRequest », nous avons envoyé des données. Encore faut-il maintenant les traiter coté serveur. Dans les exemples, on a envoyé nos données vers des scripts PHP. Nous allons continuer avec PHP.

La récupération des données dépend de la méthode employé, et du type de contenu indiqué.

content-type indiqué Données accessibles dans
multipart/form-data $_POST et $_FILES
application/x-www-form-urlencoded $_POST
Autre $HTTP_RAW_POST_DATA

Problèmes de « charset »

Il faut savoir que « XMLHttpRequest » envoie toujours les données en UTF-8, quel que soit l'encodage de la page XUL affichée. Donc vous recevrez quoi qu'il arrive toujours des données en UTF-8 dans $_POST, $_FILES ou $HTTP_RAW_POST_DATA.

Il faut donc veiller, à faire les transformations nécessaires si vous voulez stocker les données reçues, en ISO-8859-1 par exemple. En PHP, vous pouvez utiliser la fonction utf8_decode.

Pour éviter ces transformations, une solution est de travailler partout en UTF-8 : dans les fichier XUL, Javascript, PHP et de stocker vos données en UTF-8. Il ne faut donc pas oublier de :

  1. configurer votre éditeur pour qu'il sauvegarde vos sources en UTF-8 ;
  2. indiquer dans vos fichiers XML (XUL, XBL, XHTML, etc.) l'encodage, avec
 <?xml version="1.0" encoding="UTF-8" ?>
  1. éventuellement paramétrer votre base de donnée pour lui dire que les données sont en UTF-8.

Renvoi d'une réponse

Une fois les données traités, il est bon de renvoyer une réponse à « XMLHttpRequest ». Le contenu va dépendre du format utilisé pour l'échange, de ce que vous voulez récupérer côté client, etc.

Dans tous les cas, il faut toujours indiquer le type du contenu que vous allez renvoyer. En PHP, on fait cela avec la fonction header. Pour du XML, on indiquera :

  header('Content-type: text/xml');

« » et l'on recuperera la réponse dans la propriété responseXML de « XMLHttpRequest ».

Pour du texte, on indiquera

  header('Content-type: text/plain');

et l'on recuperera la réponse dans la propriété responseText de « XMLHttpRequest ».

Exemple avec du XML

Avec le script PHP suivant.

Le serveur peut renvoyer un fichier XML comme celui-ci :

 <?xml version="1.0" encoding="ISO-8859-1"?>
 <fruits>
   <pomme couleur="verte" />
   <fraise couleur="rouge" />
 </fruits>

Le script PHP sera alors (par exemple) :

 header('Content-type: text/xml');
 echo '<?xml version="1.0" encoding="ISO-8859-1"?>'
   . '<fruits>'
   . '<pomme couleur="verte" />'
   . '<fraise couleur="rouge" />'
   . '</fruits>';

Et on peut récupérer son contenu dans javascript avec :

 var fruits        = p.responseXML.documentElement;
 var couleurPomme  = fruits.getElementsByTagName('pomme')[0].getAttribute('couleur');
 var couleurFraise = fruits.getElementsByTagName('fraise')[0].getAttribute('couleur');

Utilisation en mode asynchrone

Si vous devez utiliser XMLHttpRequest depuis une extension, ou si vous ne voulez pas que l'envoi et la reception de données ne bloque votre application, vous devez utiliser XMLHttpRequest en mode asynchrone. Dans ce mode, votre code n'attend pas après le send() et passe à la suite, vous recevez une réponse via une « fonction de rappel ».

Exemple rapide

  req = new XMLHttpRequest();
  req.open('GET', 'http://xulfr.org/', true);
  req.onreadystatechange = function ()
    {
      if (req.readyState == 4)
        {
          if(req.status == 200)
            dump(req.responseText);
          else
            dump("Error loading page\n");
        }
    };
  req.send(null);

Détails

À faire.

Commentaires

MarcOlivier propose

Pour échapper les caractères des paramètres, il est recommandé d'utiliser la fonction encodeURI() plutôt que escape().

DamienHardy propose

Il peut être intéressant de fournir le bon Content-Type au serveur lors du téléversement :

 ...
 sheader += "Content-Type: " + Components.classes["@mozilla.org/mime;1"]
            .getService(Components.interfaces.nsIMIMEService)
            .getTypeFromFile(fic) + "\r\n\r\n";
 ...

SylvainPasche propose

Si l'entête contient des cacactères non-ascii, il est possible de l'encoder en UTF-8 en intercalant un nsIScriptableUnicodeConverter dans le flux. Voir http://developer.taboca.com/cases/en/XML(..) pour plus de détails.


Copyright © 2003-2013 association xulfr, 2013-2016 Laurent Jouanneau - Informations légales.

Mozilla® est une marque déposée de la fondation Mozilla.
Mozilla.org™, Firefox™, Thunderbird™, Mozilla Suite™ et XUL™ sont des marques de la fondation Mozilla.