JavaScript


API REST et AJAX

Remi Forax

Discussion client/serveur

Historiquement, un client (browser) et un serveur discute par HTTP page par page

mais l'interactivité est limitée

AJAX en 1999

Pour Asynchronous JavaScript And XML. On sépare la page et le contenu dynamique de la page. Le contenu dynamique est demandé en utilisant JavaScript.

mais on envoie des morceaux d'arbre DOM, beurk !

JSON en 2001

Douglas Crockford invente JSON un format d'échange simple indépendant d'un langage en particulier

utilise un sous-ensemble de la syntaxe des objets de JavaScript

Architecture moderne

C'est le même serveur qui envoie les fichiers statiques et répond aux appels effectuer en JavaScript. L'API est au format REST.

Server REST

REST

“Representational state transfer (REST) or RESTful Web services are one way of providing interoperability between computer systems on the Internet. REST-compliant Web services allow requesting systems to access and manipulate textual representations of Web resources using a uniform and predefined set of stateless operations.”

source: Wikipedia.org - Representational state transfer

Idée de REST

On réutilise le protocole HTTP pour faire
les appels à des services sur un serveur distant

  • client-server mais sur HTTP
  • interface uniforme
  • sans état (stateless), cacheable + load balancer

Interface uniforme

  • On nomme des resources en utilisant des URIs
  • On utilise les méthodes HTTP comme action
    GET, PUT, POST, DELETE
  • La façon dont sont stockées les données est indépendante de la représentation.
  • On utilise XML et/ou JSON comme format de transport

Exemple

On va utiliser une addresse de base api.foo.bar.com
Pour accéder au utilisateur, on va utiliser l'URI users, donc pour créer, lister, mettre à jour, supprimer des utilisateurs, on utilisera l'adresse http://api.foo.bar.com/users

Utilisation des actions

GETPUTPOSTDELETE
/users/liste les utilisateursremplace les utilisateurscrée un nouvel utilisateurdétruit les utilisateurs
/users/bobrécupère les propriétés d'un utilisateurmet à jour un utilisateursupprime un utilisateur

Côté Client: JavaScript

JSON

JSON ?

“JSON (JavaScript Object Notation) is an open-standard format that uses human-readable text to transmit data objects consisting of attribute–value pairs. It is the most common data format used for asynchronous browser/server communication.”

source: Wikipedia.org - JSON

JSON ?

“JSON (Notation objet JavaScript) est un format standard ouvert qui utilise un texte lisible par un humain pour transmettre des données objets sous forme de couples attribut/valeur. C'est le format d'échange de données le plus couramment utilisé pour la communication asynchrone entre browser et serveur.”

source : Wikipedia.org - JSON

Notation JSON

Sous ensemble de la notation sur les objets et les tableaux JavaScript qui sert de format d'échange de données

seuls les types primitifs sont suppportés, boolean, number, string, array & object.
Les noms des propriétés apparaissent entre " " !

JSON.parse

Permet de décoder en JavaScript une chaine de caractères comme un objet JSON

Asynchronous Javascript And XML

AJAX ?

“Ajax (also AJAX; short for asynchronous JavaScript and XML) is a set of web development techniques using many web technologies on the client-side to create asynchronous Web applications.”

source: Wikipedia.org - AJAX

AJAX ?

“Ajax (aussi AJAX; acronyme pour JavaScript asynchrone et XML) est un ensemble de techniques de développement web utilisant plusieurs technologies web côté client dans le but de créer des applications Web asynchrones.”

source : Wikipedia.org - AJAX

AJAX ou AJAJ

AJAX, permet en Javascript de faire des requêtes à un serveur en enregistrant une fonction qui sera exécutée plus tard, lorsque les données du serveur seront disponibles.

Le fait de faire le traitement plus tard lorsque les données seront présentes est appelé traitement asynchrone

Dans la réalité, on utilise plutôt JSON que XML comme format de transport, mais on garde le nom AJAX (au lieu de AJAJ).

XML ou JSON ?

JSON est non structuré et non validable facilement donc il est utilisé pour les transferts de petites données (en kilo)

XML est plus lourd mais structuré et validable, il est utilisé pour le transfert de données plus importantes (en méga)

fetch et Promise

Fetch

fetch(uri, option?) effectue un appel HTTP asynchrone, et then() indique quoi faire avec la réponse (une fois qu'elle arrive)


fetch("http://api.foo.bar.com/users/bob", { method: "POST" })
  .then(response => {
     if (!response.ok) {
       throw Error(response.statusText);
     }
     return ...
   })
                                        

Attention, fetch renvoie une réponse même si la réponse est Not Found

Fetch en vrai

fetch(uri, options?) fait une requête AJAX et renvoie une Promise, qui indique une réponse ou une erreur

then() et catch() permettent d'installer des closures qui seront appelées si la valeur est résolue ou si il y a une erreur.

Promesse

Une Promise est la promesse d'une valeur (ou erreur) future

Fetch + JSON

Comme fetch(), response.json() est aussi une Promise,
qui elle va parser le JSON

then() permet de combiner les promesses !

Fetch d'une image

URL.createObjectURL permet de créer une URL locale contenant les données de l'image

async / await


JavaScript 2017

Promise.resolve()/reject()

On peut initialiser une promesse directement avec une valeur (resolve()) ou une erreur (reject())

Async fonction

Async met la valeur de retour de la fonction dans une promesse si ce n'est pas déjà le cas

JavaScript 2017

JavaScript 2015

Async fonction (2)

Async met les exceptions qui sortent de la fonction dans une promesse

JavaScript 2017

JavaScript 2015

Await une promesse

await attend une promesse et met le code qui suit dans un then en tant que closure

JavaScript 2017

JavaScript 2015

Await sans async

await ne marche que à l'intérieur d'une fonction async

Async Fetch + JSON

Comme response.json() renvoie une promesse, il marche aussi avec await

comme cela pas de callback dans les callbacks

fetch et Promise.all()

await et performance

await ne permet pas de faire plusieurs requêtes en parallèle

les fetchs sont fait les uns après les autres !

Promise.all

Calcul en parallèle, le résultat est une promesse contenant le tableau des valeurs

Promise.race

La promesse résultante est calculée dès qu'une des promesses a fini

AJAX: ancienne API

Appel Asynchrone en utilisant un HttpXMLRequest


let ajax = new XMLHttpRequest();
ajax.onreadystatechange = () => {
  if (ajax.readyState != 4) { return; }
  if(ajax.status != 200) {
     // TODO NOT OK ! (ajax.status)
  } else {
     // TODO OK (ajax.responseText)
  }
};
ajax.open('GET', 'http://api.foo.bar.com/users/bob', true);
ajax.send();
                                

fetch() est plus simple ... et mieux

Promesse & AJAX à la main

On peut faire en sorte de voir un HttpXMLRequest comme une promesse

Côté Serveur

Exemple avec Express.js

express.js est une bibliothèque qui permet de créer des points d'entrée (endpoint) REST en JavaScript.
Elle utilise le "serveur" node.js

Exemple avec Express.js


let express = require('express');
let app = express();

app.get('/users', (req, res) => {
  res.send('Hello World!');
});

app.listen(3000);
                                

On crée un endpoint sur l'URI /users puis on écoute les clients sur le port TCP 3000

Créer un endpoint

on utilise les verbes get, post, put, delete (en minuscules)


app.post('/users', (req, res) => {
  ...
})
                                

app.put('/users', (req, res) => {
  ...
})
                                

app.delete('/users', (req, res) => {
  ...
})
                                

Extraire des paramètres de l'URI

la propriété params de l'objet requête permet d'obtenir les paramètres nommés extraits de l'URI


app.get('/users/:id', (req, res) => {
  console.log('user id ' + req.params.id);
})
                                

Les paramètres nommés commencent par ':' dans l'URI

Récupérer les cookies dans la requête

Les cookies sont stockés par le client et envoyés à chaque requête


app.get('/users/:id', (req, res) => {
  console.log(req.cookies);
})
                                

cookies contient tous les cookies valides pour l'URI
cookies est un objet dont les clés sont les noms des cookies

Code de la réponse

status permet d'indiquer le code HTTP de la réponse


app.get('/users/:id', (req, res) => {
  res.status(404).send('Oops');
})
                                

https://en.wikipedia.org/wiki/List_of_HTTP_status_codes

Envoyer une réponse (texte)

send permet d'envoyer une réponse textuelle


app.get('/users/:id', (req, res) => {
  res.send('OK !');
})
                                

Le Content-Length est la longeur de la chaine de caractères
Si le status n'est pas positionné, la valeur est 200

Envoyer une réponse (fichier)

sendFile permet d'envoyer un fichier en réponse


app.get('/users/:id', (req, res) => {
  res.sendFile('/absolute/path/to/ok.png');
})
                                

Le Content-Type dépend de l'extension
le Content-Length est la taille du fichier.

Envoyer une réponse (JSON)

json permet d'envoyer une réponse au format JSON


app.get('/users/:id', (req, res) => {
  let id = req.params.id;
  res.json('{ user: ' + id + ' }');
})
                                

Le Content-Type est application/json

Envoyer des entêtes de réponse

Sur l'objet réponse, set et append permettent d'ajouter des entêtes de réponse.


app.get('/users/:id', (req, res) => {
  res.set('Set-Cookie', 'foo=bar; Path=/; HttpOnly');
})
                                

HttpOnly veut dire pas dans les requêtes AJAX !

Content-Type

type permet d'indiquer le Content-Type.


app.get('/users/:id', (req, res) => {
  res.type('text/html').send('Ok');
})