Pewarta Proxy

📋 Server Request & Proxy Logs

Loading active proxy settings...
Developed by Khairul Adha (adhexa.id)
Mengambil log server...

⚙ Pengaturan Proxy Target

23 octobre 2022

Fetch

JavaScript peut envoyer des requĂȘtes rĂ©seau au serveur et charger de nouvelles informations chaque fois que nĂ©cessaire.

Par exemple, nous pouvons utiliser une requĂȘte rĂ©seau pour :

  • Soumettre une commande,
  • Charger des informations utilisateur,
  • Recevoir les derniĂšres mises Ă  jour du serveur,
  • 
etc.


 Et tout cela sans recharger la page !

Il y a un terme gĂ©nĂ©rique “AJAX” (abrĂ©gĂ© de Asynchronous JavaScript And XML) pour les requĂȘtes rĂ©seau Ă  partir de JavaScript. Cependant nous n’avons pas besoin d’utiliser XML : le terme vient de l’ancien temps, c’est pourquoi ce mot est lĂ . Vous avez peut-ĂȘtre dĂ©jĂ  entendu ce terme.

Il existe plusieurs façons d’envoyer une requĂȘte rĂ©seau et d’obtenir des informations du serveur.

La mĂ©thode fetch() est moderne et polyvalente, nous allons donc commencer avec celle-ci. Elle n’est pas prise en charge par les anciens navigateurs (peut ĂȘtre polyfilled), mais trĂšs bien prise en charge par les navigateurs modernes.

La syntaxe de base est :

let promise = fetch(url, [options])
  • url – l’URL cible.
  • options – paramĂštres facultatifs : mĂ©thode, en-tĂȘtes, etc


Sans options, c’est une simple requĂȘte GET, tĂ©lĂ©chargeant le contenu de l’url.

Le navigateur dĂ©marre la requĂȘte immĂ©diatement et renvoie une promesse que le code appelant devrait utiliser pour obtenir le rĂ©sultat.

Obtenir une réponse est généralement un processus en deux étapes.

PremiĂšrement, la promise, renvoyĂ©e par fetch, se rĂ©sout avec un objet de la classe intĂ©grĂ©e Response dĂšs que le serveur rĂ©pond avec des en-tĂȘtes.

À ce stade, nous pouvons vĂ©rifier l’état HTTP, pour voir s’il est rĂ©ussi ou non, vĂ©rifier les en-tĂȘtes, mais nous ne disposons pas encore du corps.

La promesse rejette si le fetch n’a pas pu faire de requĂȘte HTTP, par exemple problĂšmes de rĂ©seau, ou si l’adresse n’existe pas. Les statuts HTTP anormaux, tels que 404 ou 500, ne provoquent pas d’erreur.

Nous pouvons voir l’état HTTP dans les propriĂ©tĂ©s de rĂ©ponse :

  • status – Code d’état HTTP, par exemple 200.
  • ok – boolĂ©en, true si le code d’état HTTP est 200-299.

Par exemple :

let response = await fetch(url);

if (response.ok) { // if HTTP-status is 200-299
  // obtenir le corps de réponse (la méthode expliquée ci-dessous)
  let json = await response.json();
} else {
  alert("HTTP-Error: " + response.status);
}

DeuxiÚmement, pour obtenir le corps de la réponse, nous devons utiliser un appel de méthode supplémentaire.

Response fournit plusieurs méthodes basées sur les promesses pour accéder au corps dans différents formats :

  • response.text() – lit la rĂ©ponse et retourne sous forme de texte,
  • response.json() – analyse la rĂ©ponse en JSON,
  • response.formData() – retourne la rĂ©ponse en tant que objet FormData (expliquĂ© dans le chapitre suivant),
  • response.blob() – retourne la rĂ©ponse en tant que Blob (donnĂ©e binaire avec type),
  • response.arrayBuffer() – retourne la rĂ©ponse en tant que ArrayBuffer (reprĂ©sentation de bas niveau de donnĂ©e binaire),
  • aditionellement, response.body est un objet ReadableStream, qui permet de lire le corps morceau par morceau, nous verrons un exemple plus tard.

Par exemple, obtenons un objet JSON avec les derniers commits de GitHub :

let url = 'https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits';
let response = await fetch(url);

let commits = await response.json(); // lire le corps de réponse et analyser en JSON

alert(commits[0].author.login);

Ou, la mĂȘme chose sans await, en utilisant la syntaxe des promesses pures :

fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits')
  .then(response => response.json())
  .then(commits => alert(commits[0].author.login));

Pour obtenir la réponse en texte, await response.text() au lieu de .json() :

let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits');

let text = await response.text(); // lire le corps de la réponse sous forme de texte

alert(text.slice(0, 80) + '...');

En tant que vitrine pour la lecture au format binaire, rĂ©cupĂ©rons et affichons une image du logo de “fetch” specification (voir le chapitre Blob pour plus de dĂ©tails sur les opĂ©rations de Blob):

let response = await fetch('/article/fetch/logo-fetch.svg');

let blob = await response.blob(); // télécharger en tant qu'objet Blob

// create <img> for it
let img = document.createElement('img');
img.style = 'position:fixed;top:10px;left:10px;width:100px';
document.body.append(img);

// l'afficher
img.src = URL.createObjectURL(blob);

setTimeout(() => { // le cacher aprĂšs 3 secondes
  img.remove();
  URL.revokeObjectURL(img.src);
}, 3000);
Important :

Nous ne pouvons choisir qu’une seule mĂ©thode de lecture du corps.

Si nous avons déjà la réponse avec response.text(), alors response.json() ne fonctionnera pas, car le contenu du corps a déjà été traité.

let text = await response.text(); // corps de la réponse consommé
let parsed = await response.json(); // echec (déjà consommé)

En-tĂȘtes de rĂ©ponse

Les en-tĂȘtes de rĂ©ponse sont disponibles dans un objet d’en-tĂȘtes de type Map-like response.headers.

Ce n’est pas exactement un Map, mais il a des mĂ©thodes similaires pour obtenir des en-tĂȘtes individuels par nom ou les parcourir :

let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits');

// get one header
alert(response.headers.get('Content-Type')); // application/json; charset=utf-8

// iterate over all headers
for (let [key, value] of response.headers) {
  alert(`${key} = ${value}`);
}

En-tĂȘtes de requĂȘtes

Pour dĂ©finir un en-tĂȘte de requĂȘte dans fetch, nous pouvons utiliser l’option headers. Il a un objet avec des en-tĂȘtes sortants, comme ceci :

let response = fetch(protectedUrl, {
  headers: {
    Authentication: 'secret'
  }
});


 Mais il y a une liste d’en-tĂȘtes HTTP interdits que nous ne pouvons pas dĂ©finir :

  • Accept-Charset, Accept-Encoding
  • Access-Control-Request-Headers
  • Access-Control-Request-Method
  • Connection
  • Content-Length
  • Cookie, Cookie2
  • Date
  • DNT
  • Expect
  • Host
  • Keep-Alive
  • Origin
  • Referer
  • TE
  • Trailer
  • Transfer-Encoding
  • Upgrade
  • Via
  • Proxy-*
  • Sec-*

Ces en-tĂȘtes assurent un HTTP correct et sĂ»r, ils sont donc contrĂŽlĂ©s exclusivement par le navigateur.

RequĂȘtes POST

Pour faire une requĂȘte POST, ou une requĂȘte avec une autre mĂ©thode, nous devons utiliser les options fetch :

  • method – HTTP-method, par exemple POST,
  • body – le corps de la requĂȘte, un parmi ceux-ci :
    • une chaĂźne de caractĂšres (par exemple encodĂ© en JSON),
    • un objet FormData, pour soumettre les donnĂ©es en tant que multipart/form-data,
    • Blob/BufferSource pour envoyer des donnĂ©es binaires,
    • URLSearchParams, pour soumettre les donnĂ©es au format x-www-form-urlencoded, rarement utilisĂ©.

Le format JSON est utilisé la plupart du temps.

Par exemple, ce code soumet l’objet user en JSON :

let user = {
  name: 'John',
  surname: 'Smith'
};

let response = await fetch('/article/fetch/post/user', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json;charset=utf-8'
  },
  body: JSON.stringify(user)
});

let result = await response.json();
alert(result.message);

Veuillez noter que si la requĂȘte body est une chaĂźne de caractĂšres, alors l’en-tĂȘte Content-Type est dĂ©fini sur text/plain;charset=UTF-8 par dĂ©faut.

Mais, si nous envoyons du JSON, nous utiliserons Ă  la place l’option headers pour envoyer application/json, le bon Content-Type pour les donnĂ©es encodĂ©es en JSON.

Envoi d’une image

Nous pouvons également soumettre des données binaires avec fetch en utilisant des objets Blob ou BufferSource.

Dans cet exemple, il y a un <canvas> oĂč nous pouvons dessiner en dĂ©plaçant une souris dessus. Un clic sur le bouton “submit” envoie l’image au serveur :

<body style="margin:0">
  <canvas id="canvasElem" width="100" height="80" style="border:1px solid"></canvas>

  <input type="button" value="Submit" onclick="submit()">

  <script>
    canvasElem.onmousemove = function(e) {
      let ctx = canvasElem.getContext('2d');
      ctx.lineTo(e.clientX, e.clientY);
      ctx.stroke();
    };

    async function submit() {
      let blob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png'));
      let response = await fetch('/article/fetch/post/image', {
        method: 'POST',
        body: blob
      });

      // le serveur répond avec confirmation et la taille de l'image
      let result = await response.json();
      alert(result.message);
    }

  </script>
</body>

Veuillez noter qu’ici, nous ne dĂ©finissons pas l’en-tĂȘte Content-Type manuellement, car un objet Blob a un type intĂ©grĂ© (ici image/png, tel que gĂ©nĂ©rĂ© par toBlob). Pour les objets Blob, ce type devient la valeur de Content-Type.

La fonction submit() peut ĂȘtre réécrite sans async/await comme ceci :

function submit() {
  canvasElem.toBlob(function(blob) {
    fetch('/article/fetch/post/image', {
      method: 'POST',
      body: blob
    })
      .then(response => response.json())
      .then(result => alert(JSON.stringify(result, null, 2)))
  }, 'image/png');
}

Résumé

Une requĂȘte fetch typique se compose de deux appels await :

let response = await fetch(url, options); // se rĂ©sout avec des en-tĂȘtes de rĂ©ponse
let result = await response.json(); // lit le corps en tant que JSON

Ou, sans await :

fetch(url, options)
  .then(response => response.json())
  .then(result => /* process result */)

Propriétés de réponse :

  • response.status – Code HTTP de la rĂ©ponse,
  • response.ok – true est le statut 200-299.
  • response.headers – objet Map-like avec en-tĂȘtes HTTP.

Méthodes pour obtenir le corps de réponse :

  • response.text() – retourne la rĂ©ponse sous forme de texte,
  • response.json() – analyse la rĂ©ponse en tant qu’objet JSON,
  • response.formData() – retourne la rĂ©ponse en tant qu’objet FormData (encodage multipart/form-data, voir le chapitre suivant),
  • response.blob() – retourne la rĂ©ponse en tant que Blob (donnĂ©es binaires avec type),
  • response.arrayBuffer() – retourne la rĂ©ponse en tant que ArrayBuffer (donnĂ©es binaires de bas niveau),

Options de fetch jusque lĂ  :

  • method – MĂ©thode HTTP,
  • headers – un objet avec en-tĂȘtes de requĂȘte (aucun en-tĂȘte n’est autorisĂ©),
  • body – les donnĂ©es Ă  envoyer (corps de la demande) en tant que string, FormData, BufferSource, Blob ou objet UrlSearchParams.

Dans les chapitres suivants, nous verrons plus d’options et de cas d’utilisation de fetch.

Exercices

CrĂ©ez une fonction asynchrone getUsers(names), qui obtient un tableau de connexions GitHub, rĂ©cupĂšre les utilisateurs de GitHub et renvoie un tableau d’utilisateurs GitHub.

L’URL GitHub avec les informations utilisateur pour la donnĂ©e USERNAME est : https://api.github.com/users/USERNAME.

Il y a un exemple de test dans la sandbox.

Détails importants :

  1. Il devrait y avoir une requĂȘte fetch par utilisateur.
  2. Les demandes ne doivent pas s’attendre les unes les autres. Pour que les donnĂ©es arrivent le plus tĂŽt possible.
  3. Si une requĂȘte Ă©choue, ou si l’utilisateur n’existe pas, la fonction doit retourner null dans le tableau de rĂ©sultats.

Open a sandbox with tests.

Pour récupérer un utilisateur, nous avons besoin de : fetch('https://api.github.com/users/USERNAME').

Si la rĂ©ponse a le statut 200, appelons .json() pour lire l’objet JS.

Sinon, si un fetch échoue, ou si la réponse a un statut différent de 200, nous renvoyons simplement null dans le tableau de résutats.

Voici donc le code :

async function getUsers(names) {
  let jobs = [];

  for(let name of names) {
    let job = fetch(`https://api.github.com/users/${name}`).then(
      successResponse => {
        if (successResponse.status != 200) {
          return null;
        } else {
          return successResponse.json();
        }
      },
      failResponse => {
        return null;
      }
    );
    jobs.push(job);
  }

  let results = await Promise.all(jobs);

  return results;
}

Veuillez noter : l’appel .then est directement attachĂ© Ă  fetch, de sorte que lorsque nous avons la rĂ©ponse, il n’attend pas d’autres fetches, mais commence Ă  lire .json() immĂ©diatement.

Si nous avions utilisĂ© await Promise.all(names.map(name => fetch(...))), et appelĂ© .json() sur les rĂ©sultats, il aurait attendu que tous les fetches rĂ©pondent. En ajoutant .json() directement Ă  chaque fetch, nous nous assurons que les fetches individuels commencent Ă  lire les donnĂ©es en JSON sans s’attendre les uns les autres.

C’est un exemple de la façon dont l’API Promise de bas niveau peut toujours ĂȘtre utile mĂȘme si nous utilisons principalement async/wait.

Ouvrez la solution avec des tests dans une sandbox.

Carte du tutoriel

Commentaires

lire ceci avant de commenter

  • Si vous avez des amĂ©liorations Ă  suggĂ©rer, merci de soumettre une issue GitHub ou une pull request au lieu de commenter.
  • Si vous ne comprenez pas quelque chose dans l'article, merci de prĂ©ciser.
  • Pour insĂ©rer quelques bouts de code, utilisez la balise <code>, pour plusieurs lignes – enveloppez-les avec la balise <pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepen
)