• Aucun résultat trouvé

[PDF] Cours Ajax Control avec exemples d'application | Cours informatique

N/A
N/A
Protected

Academic year: 2021

Partager "[PDF] Cours Ajax Control avec exemples d'application | Cours informatique"

Copied!
20
0
0

Texte intégral

(1)

Le modèle classique du Web

Dans le modèle standard, chaque requète HTTP génère le chargement d'une nouvelle page: le navigateur n'est rien d'autre qu'un "afficheur" de pages, et tout le travail revient au serveur. Avantages:

 simplicité du client

 le serveur fait tout le travail de génération des pages : modèle assez sûr (sécurité) Inconvénients:

 transmission fréquente de pages entières, donc consommation de bande passante  attente du chargement des pages parfois long, à un moment la fenêtre affiche une

page blanche...

 ergonomie dégradée par rapport à une application native

Le modèle AJAX

AJAX veut dire "Asynchronous Javascript and XML". Dans ce modèle, le code javascript d'une page peut effectuer des requètes HTTP, sans change de page. On travaille alors en mode asynchrone: le code javascript tourne en tâche de fond, et les requètes HTTP faite en javascript ne sont pas blocantes (on leur associe une fonction de callback).

Dans ce modèle, plutôt que quelque chose de statique une page est vraiment un "programme" javascript qui va faire des appels sur le web de façon autonome.

Avantages:

 La page n'échange que les données qui ont besoin de changer: un e-mail, une image, etc.On a donc une bande passante et temps de transfert réduits

 La page peut rester opérationnelle pendant l'attente d'une réponse à une requête Inconvénients:

 Nécessité d'avoir un moteur JavaScript performant dans le navigateur → course actuelle aux optimisations

 Sécurité: attention à ne pas faire confiance au client !  Contenu pas indexable par les moteurs de recherche  Bouton « retour » non fonctionnel par défaut

Exemples

Les services google sont de façon générale des applications AJAX. Par exemple:  GMail ne charge que les messages, pas la page entière

 Google Maps ne charge que des portions carrées des cartes/images satellite

 («tuiles»). Même lorsque des dalles sont en cours de chargement on peut continuer à déplacer/zoomer sur la carte

 Le moteur de recherche affiche des suggestions au cours de la frappe. L'obtention se fait en arrière-plan, ce qui n'empêche pas de continuer à taper (fonction de callback!) Mais essentiellement tous les services modernes du web le sont aussi (au moins en partie):

 Flickr: les images sont chargeés dynamiquement  Twitter: le flux des tweets est rafraîchi à la volée  Tous les jeux tels agar.io sont de l'AJAX pur  ...

Et en particulier, la grosse majorité propose des interface pour faire des requêtes directement:  l'API Flickr avec la recherche de photos

(2)

 l'API google map

 l'API d'OpenWeatherMap, pour la météo  l'API de Twitter

 ...

Notez que pour utiliser ces services, il vous sera en général demandé de vous créer un ID sur le site. C'est gratuit, si vous utilisez le service dans des limites raisonnables.

Requêtes HTPP en AJAX

L'objet essentiel qui permet de faire des requêtes HTTP en javascript est XMLHttpRequest. On doit faire un certain nombre de choix:

 mode synchrone ou asynchrone  type de requête: POST, GET, PUT  format d'échange: XML ou JSON

Mode synchrone

Tant que la requête n'a pas abouti, la fenêtre reste bloquée et l'utilisateur ne peut pas interagir avec elle.

Mode asynchrone

Dans ce mode, la requête est "instantanée", et l'exécution du code javasript peut continuer sans interruption. L'interaction de l'utilisateur avec la page n'est pas impactée.

L'idée est d'utiliser une fonction de callback qui sera utilisée pour traiter la requête lorsqu'elle aura abouti.

(3)

Des précautions sont à prendre dans ce mode:

 rien ne garantit que les résultats de la précédente requête sont arrivés  l'ordre des réponses peut même être inversé

D'où la nécessité de prendre en compte ces aléas: ils ne doivent pas perturber le fonctionnement de l'application

Par exemple, si on tape « a » puis « b » dans le champ de recherche Google, si le résultat de la recherche pour « a » arrive après celui pour « ab », il ne faut pas afficher les résultats pour « a » !

GET et POST

Il s'agit de deux modes principaux pour passer des paramètres à une requête sur le web. GET

 Les paramètres éventuels sont dans l'URL: "url?para1=val1&para2=val2&..."  L'URL peut être mémorisée dans les favoris/marque-pages, elle peut être indexée par

les moteurs de recherche

 ne doit pas provoquer de mise à jour sur le serveur, uniquement l'obtention de données POST

 En plus de l'URL on peut envoyer des données au serveur (XML, JSON...)  convient bien à la modification de données

AJAX avec jQuery

Dû à des implémentations différentes suivant les navigateurs, il n'est pas recommandé d'utiliser directement l'objet XMLHttpRequest. À la place, on utilise... La librairie jQuery bien sûr. Celle-ci masque les incompatibilités entre navigateurs.

Une requête AJAX se fait comme suit:

1

2

3

4

5

6

$.ajax({ url: "http://example.com/myService",

data: {para1 : "val1", para2 : "val2", ...}, dataType: "xml",

type: "GET",

success: processSuccess, error: processError,

(4)

7

8

9

10

11

12

13

});

functionprocessSuccess(data) { ... }

functionprocessError(jqXHR, textStatus, errorThrown) { console.log(errorThrown + " : "+ textStatus);

}

On donne deux fonctions de callback: une pour traiter les données dans elles sont arrivées, et une pour traiter les erreurs éventuelles. Cette fonction attends en argument l'objet httpRequest, et deux chaines de caractères qui contiennent explicitent l'erreur.

On retrouve le type de l'appel qui peut être GET ou POST, et les paramètres de requête peuvent être donnés dans l'attribut data

Le dataType décrit le type de réponse attendu, qui peut être:  xml : l'appel rend alors un objet DOM correspondant  json : l'appel renvoie un object javascript correspondant

 jsonp : comme "json", mais encapsulé dans une balises <script> créée à la volée (voir plus bas)

 text : l'appel renvoie une chaine de caractère.

Racourcis

.get()

Un raccourci est la fonction .get() que vous avez déjà rencontrée. Cette méthode effectue une requête HTTP GET de façon asynchrone.

.load()

Cette fonction charge un fichier (HTML) et l'insère dans l'arbre DOM. Voir la doc pour plus d'information. Dans sa version simple, elle s'utilise comme suit:

1

$("#result").load("monfichier.html");

Le contenu de monfichier.html va être placer à l'intérieur du noeud d'id #result.

Requêtes inter-domaine

Par défaut, les requêtes inter-domaines sont interdites: ''Same-Origin Policy''. Donc on ne peut pas faire en javascript une HttpRequest à un site arbitraire. La raison est simple: comme on peut incorporer à une page des fichiers javascripts de provenance quelconque, ces fichiers pourraient contenir des scripts malicieux, qui par exemple récupéreraient des identifiants et mots de passe.

Contourner la restriction

La restriction ne s'applique que lorsque la page est hébergée sur un serveur externe. En principe, il n'y a pas de restriction lorsque vous travaillez en local (c'est à dire si le fichier HTML se trouve sur votre ordinateur).

Mais si la page est hébergée en ligne, on va avoir le problème. Les méthodes standards pour y répondre sont les suivantes.

Avec un proxy sur le serveur

Une solution simple pour contourner la restriction est de demamder au serveur de faire la requête... Si cela résoud le problème (puisque le serveur peut faire ce qu'il veut), cela

(5)

complexifie un peu le travail du serveur et rends l'appel à un service externe un peu moins fluide.

CORS

La technique CORS (Cross Origin Resource Sharing) est un entête HTTP

particulier: Access-Control-Allow-Origin qui permet d'indiquer quels sont les sites externes autorisés pour les appels AJAX.

Encore une fois, il s'agit d'une configuration au niveau du serveur. Si c'est beaucoup moins lourd pour le serveur qu'un proxy, cela nécessite une configuration spéciale et requière d'avoir un navigateur qui comprend l'entête CORS (de fait, à l'heure actuelle tous les navigateurs le comprenne).

Le hack JSONP

Les deux techniques précédentes nécessitent de pouvoir configurer le serveur. Une solution alternative, purement AJAX, est d'utiliser la technique JSONP, pour JSON with Padding. L'idée est la suivante: si javascript ne peut pas faire une requête à un serveur qui ne soit pas celui à l'origine de la page, le navigateur peut bien sûr charger des resources issues de sites quelconques: images, mais aussi fichiers CSS ou javascript.

En résumé, il y a deux façons d'obtenir une resource javascript sur internet à partir d'une page web:

 Par une balise <script type="text/javascript" src="...">. La source peut être quelconque. Donc par exemple, on peut aller chercher une resource sur un serveur de google, de twitter, ...

 Par un appel AJAX avec un objet HttpRequest, depuis javascript. Ceci n'est en général autorisé que pour faire un appel au domaine à laquelle la page appartient. Donc on est en général coincé: on ne peut pas faire appel à google par là.

L'astuce JSONP consiste à remarquer que l'on peut ajouter des noeuds quelconques à l'arbre DOM de la page... Donc en particulier rien ne nous interdit d'ajouter une balise <script> à la volée, avec donc une adresse potentiellement quelconque. C'est là que réside l'astuce: il suffit que le service web que l'on interroge puisse renvoyer la réponse attendue dans un fichier javascript...

Donc une application web peut renvoyer:

Par exemple, essayez les appels suivants à l'application openweathermap, qui fournit des données météorologiques:  XML: http://api.openweathermap.org/data/2.5/weather?q=London,uk&appid=22e21ef6495 26ef2b1be4db6d2b0857d&mode=xml  JSON: http://api.openweathermap.org/data/2.5/weather?q=London,uk&appid=22e21ef649 526ef2b1be4db6d2b0857d&mode=json  JSONP: http://api.openweathermap.org/data/2.5/weather?q=London,uk&appid=22e21ef64 9526ef2b1be4db6d2b0857d&callback=maFonctionQuiTraiteLaReponse

Dans la dernière version, la réponse du serveur est un fichier javascript très simple qui contient un appel à la fonction demandée (vous pouvez choisir le nom que vous voulez), avec comme argument le contenu de la requête:

1

maFonctionQuiTraiteLaReponse({"coord":{"lon":-0.13,"lat":51.51},"weather": ...);

En bref

Une requête à une application web peut renvoyer  du XML

 du JSON

 ... mais aussi un petit fichier javascript qui appelle une fonction locale.

De façon générale, l'appel JSONP est le plus "portable", même si la majeure partie des services web qui proposent ce type d'accès à leur base de donnée le font avec les bons entêtes CORS.

(6)

Un exemple AJAX complet

Dans cet exemple, nous allons mettre en oeuvre des appels à l'application openweathermap vue plus haut afin d'obtenir la météo à Londres. Nous allons faire un appel avec le mode JSON, avec le mode XML, puis avec le mode JSONP.

JSON

La chaine de caractère rendue par le serveur à la requête

http://api.openweathermap.org/data/2.5/weather?q=London,uk&appid=22e21ef649526ef2b1b e4db6d2b0857d&mode=json

ressemble à

{"coord": {"lon": -0.13,"lat": 51.51},"weather": [{"id": 800,"main": "Clear","description": "Sky is Clear","icon": "01d"}],"base": "cmc stations","main": {"temp": 277.211,"pressure": 1028.87,"humidity": 84,"temp_min": 277.211,"temp_max": 277.211,"sea_level": 1039.14,"grnd_level": 1028.87},"wind": {"speed": 7.08,"deg": 353.501},"clouds": {"all": 0},"dt": 1455538262,"sys": {"message": 0.0077,"country": "GB","sunrise": 1455520476,"sunset": 1455556528},"id": 2643743,"name": "London","cod": 200}

Donc la description du temps se trouve dans objectJsonEnQuestion.weather[0].description (voir la doc pour plus de détail).

Un exemple simple d'utilisation avec cet appel en mode JSON pourrait être le suivant:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

<!DOCTYPE html> <html> <head> <metacharset="utf-8"> <title>Test AJAX</title> <scripttype="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script> <script> function uneFonction(ville) { alert(ville.weather[0].description); }

function traiteErreur(jqXHR, textStatus, errorThrown) { alert("Erreur " + errorThrown + " : " + textStatus); }

$.ajax({

url : "http://api.openweathermap.org/data/2.5/weather",

data : { q : "London,uk", appid : "22e21ef649526ef2b1be4db6d2b0857d", mode : "json" }, dataType : "json", success : uneFonction, error : traiteErreur }); </script> </head> <body> <h1>Test AJAX</h1> </body> </html>

XML

(7)

http://api.openweathermap.org/data/2.5/weather?q=London,uk&appid=22e21ef649526ef2b1b e4db6d2b0857d&mode=xml ressemble à

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

<current>

<cityid="2643743"name="London"> <coordlon="-0.13"lat="51.51"/> <country>GB</country>

<sunrise="2016-02-15T07:14:35"set="2016-02-15T17:15:29"/> </city>

<temperaturevalue="277.211"min="277.211"max="277.211"unit="kelvin"/> <humidityvalue="84"unit="%"/>

<pressurevalue="1028.87"unit="hPa"/> <wind>

<speedvalue="7.08"name="Moderate breeze"/> <gusts/>

<directionvalue="353.501"code=""name=""/> </wind>

<cloudsvalue="0"name="clear sky"/> <visibility/>

<precipitationmode="no"/>

<weathernumber="800"value="Sky is Clear"icon="01d"/> <lastupdatevalue="2016-02-15T12:19:55"/>

</current>

Notez d'abord combien c'est plus lisible (bien que plus verbeux). En tous les cas, la description du temps s'accède avec jQuery à

1

$(laVariableDOM).find("weather").attr("value")

Donc la même chose qu'au dessus mais en utilisant le mode XML donnerait:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

<!DOCTYPE html> <html> <head> <metacharset="utf-8"> <title>Test AJAX</title> <scripttype="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script> <script> function uneFonction(ville) { alert($(ville).find("weather").attr("value")); }

function traiteErreur(jqXHR, textStatus, errorThrown) { alert("Erreur " + errorThrown + " : " + textStatus); }

$.ajax({

url : "http://api.openweathermap.org/data/2.5/weather",

data : { q : "London,uk", appid : "22e21ef649526ef2b1be4db6d2b0857d", mode : "xml" }, dataType : "xml", success : uneFonction, error : traiteErreur }); </script> </head> <body>

(8)

22

23

24

25

<h1>Test AJAX</h1> </body> </html>

JSONP

Comme expliqué plus haut, JSONP permet de demander la réponse encapsulé dans un appel de fonction au serveur. Donc la chaine de caractère rendue par le serveur à la requête

http://api.openweathermap.org/data/2.5/weather?q=London,uk&appid=22e21ef649526ef2b1b e4db6d2b0857d&callback=uneFonction

ressemble à

uneFonction({"coord": {"lon": -0.13,"lat": 51.51},"weather": [{"id": 800,"main": "Clear","description": "Sky is Clear","icon": "01d"}],"base": "cmc stations","main": {"temp": 277.211,"pressure": 1028.87,"humidity": 84,"temp_min": 277.211,"temp_max": 277.211,"sea_level": 1039.14,"grnd_level": 1028.87},"wind": {"speed": 7.08,"deg": 353.501},"clouds": {"all": 0},"dt": 1455538262,"sys": {"message": 0.0077,"country": "GB","sunrise": 1455520476,"sunset": 1455556528},"id": 2643743,"name": "London","cod": 200})

Il suffit donc en théorie pour utiliser le mode de faire cette requête comme on ferait un appel à un fichier javascript: son contenu va simplement être exécuté. La seul contrainte est donc de demander l'appel d'une fonction déjà définie...

1

2

3

4

5

6

7

8

9

10

11

12

13

<!DOCTYPE html> <html> <head> <metacharset="utf-8"> <title>Test AJAX</title>

<scripttype="text/javascript"src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script> <script>

function uneFonction(ville) { alert(ville.weather[0].description); } </script> <scripttype="text/javascript" src="http://api.openweathermap.org/data/2.5/weather?q=London,uk&appid=22e21ef649526ef2b1be4db6d2b0857d&callback=uneFonction"></script> </head> <body> <h1>Test AJAX</h1> </body> </html>

On définit une fonction uneFonction qui va chercher une information dans l'objet généré par le serveur (allez voir la doc). Puis on charge un fichier javascript, qui n'est autre que l'appel de cette fonction avec l'information donnée par le serveur:

1

uneFonction({"coord":{"lon":-0.13,"lat":51.51},"weather": ...); Bien sûr, à ce niveau nous ne sommes pas encore AJAX... Mais maintenant, plutôt que d'écrire cette balise script en dûr dans le fichier html, on peut le faire à la volée. Soit à la main:

1

2

3

4

5

6

7

8

<!DOCTYPE html> <html> <head> <metacharset="utf-8"> <title>Test AJAX</title>

<scripttype="text/javascript"src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script> <script>

function uneFonction(ville) { alert(ville.weather[0].description); }

(9)

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

var b = $("<script>", { type : "text/javascript", src : "http://api.openweathermap.org/data/2.5/weather?q=London,uk&appid=22e21ef649526ef2b1be4db6d2b0857d&callback=uneFonction" }); $("head").append(b); }

$(document).ready(makeBalise); </script> </head> <body> <h1>Test AJAX</h1> </body> </html>

ou, de façon équivalente, avec un appel JSONP à .ajax(): ?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

<!DOCTYPE html> <html> <head> <metacharset="utf-8"> <title>Test AJAX</title>

<scripttype="text/javascript"src="https://code.jquery.com/jquery-1.12.0.min.js"></script> <script>

function uneFonction(ville) {

alert(ville.weather[0].description); }

function traiteErreur(jqXHR, textStatus, errorThrown) { alert("Erreur " + errorThrown + " : " + textStatus); }

function appelWeather() {

$.ajax({ url: "http://api.openweathermap.org/data/2.5/weather",

data : { q : "London,uk", appid : "22e21ef649526ef2b1be4db6d2b0857d" }, dataType: "jsonp", success: uneFonction, error: traiteErreur }); }

$(document).ready(appelWeather); </script> </head> <body> <h1>Test AJAX</h1> </body> </html>

Une mini-appli de météo temps-réel

On peut bien sûr faire un appel AJAX de façon interactive. Plutôt que d'appeler le serveur de façon péremptoire au début, on peut faire un petit formulaire pour interroger le serveur de façon interactive. Dans cet exemple: weather.html vous trouverez la méthode JSONP implémentée.

Dans le champ de texte, rentrez par exemple "Nice,fr", ou "Paris,fr", ou "Rome,it": en principe, le paragraphe en dessous devrait se mettre à jour en rapport avec la météo courante.

(10)

Coté serveur: javascript pour les

services web

Introduction

Dans ce tutoriel, nous allons voir le framework Node.js, qui permet de développer rapidement un web service répondant avec des données formatées ou des pages internet.

Le modèle standard

Dans un site web traditionnel, le serveur répond aux requêtes des clients en renvoyant le contenu d'un fichier: Si un client requiert l'adresse http://domain.com/page.html, typiquement le serveur du domaine domain.com va aller chercher le fichier page.html, le lire et l'envoyer sur le réseau en réponse. C'est ce que propose par défaut les serveurs web tels que le serveur ISS de Microsoft, Apache, ou encore le léger lighttpd.

Un modèle un tout petit peu plus dynamique permet à un serveur internet d'exécuter un script pour répondre à une requête, et à rediriger la sortie du script en réponse à la requête. En général, l'interface utilisée est CGI (pour Common Gateway Interface). Tous les serveurs permettent cela.

Quelques langages usuels de scripts:  Perl (avec l'extension ".pl")  PHP (avec l'extension ".php")

 ASP (pour

Active Server Pages

, essentiellement avec le serveur IIS)  Mais aussi Python, Ruby...

Le modèle Node.js

À l'inverse de ce schéma, Node.js est un programme autonome. Il est basé sur le moteur d'exécution JavaScript V8 de Google et a été créé en 2009 par Ryan Dahl (Joyent Inc.). Écrit en C++, c'est essentiellement une plateforme d'exécution javascript avec des APIs

spécialisées pour gérer le fait d'être un serveur: la gestion des connections réseaux, mais aussi le système de fichiers,... On a donc pas de "comportement par défaut", et tout doit se programmer en javascript.

Les grands principes sous-tendant Node.js sont:  Éxécution pilotée par les événements

 Appels de fonctions asynchrones (utilisation de callback)  Entrées/sorties non bloquantes

Les deux premiers sont "standards" dans la philosophie javascript. Le troisième principe est essentiel de la part d'un serveur: on souhaite qu'il reste réactif autant que possible.

Des exemples de l'utilisation de Node.js en production:  Un joli site :

https://tweetping.net/

 Mais aussi :

Linkedin

 Et

Microsoft Azure

 Et

Walmart

 Et

Paypal

 ...

(11)

Un serveur Node.js consiste au minimum en  le programme nodejs

 un fichier javascript

Un script pour Node.js va faire les choses suivantes:

 Initialiser des objets correspondant aux librairies avec la fonction require  par exemple, la librairie http qui gère le protocole HTTP

 définir un serveur  lancer le serveur

Par exemple, voila un code minimal.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

varhttp = require('http');

varserv = http.createServer( //création d'un serveur web

function(req, res) { //callback sur les requêtes HTTP //construction d'une réponse HTTP

res.writeHead(200, {'Content-Type': 'text/plain'}); res.write('Hello world !');

res.end(); //envoi de la réponse }

);

serv.listen(8000); //commence à accepter les requêtes

console.log("Server running at http://localhost:8000");

Pour l'exécuter:

 Placez ce texte dans monserveur.js (avec gedit ou jedit par exemple)  Ouvrez un terminal

 Rendez-vous dans le dossier où vous avez sauvé le fichier:

1

cd le/répertoire/en/question  Tappez la commande

1

nodejs monserveur.js

(12)

1

http://localhost:8000

 Si le message "Hello World" s'affiche: bravo, vous avez un serveur web.

Le concept

Comme dit plus haut, le script Node.js ne fait rien tout seul: il ne fait que réagir à des événements. Ici, on crée un objet serv qui contient les propriétés pour de fait être un serveur. La principale caractéristique d'un serveur est la façon dont il réagit aux requêtes: c'est le but de la fonction (non nommée) en argument de la méthode createServer. Cette fonction est appelée à chaque fois qu'un client se connecte: elle prend en argument req, objet qui décrit la requête, et res, objet que la fonction va peupler et qui décrit la réponse HTTP (voir le cours/tuto HTTP).

 L'objet req est de la classe http.IncomingMessage

 L'objet res est de la classe http.ServerResponse

La deuxième chose que fait le le script est de demander au serveur de l'objet serv d'écouter le port 8000.

Donc le serveur ne fait rien tant qu'un client ne se connecte pas. Son comportement est ensuite complètement décrit par la fonction non-nommée liée par la méhode createServer.

Voir l'échange HTTP

Avec Firefox, vous pouvez utiliser par exemple l'extension Live Http Headers.

Boucle de traitement

 Un seul programme (un seul thread) traite les événements dans l'ordre de leur occurrence  Les traitements qui concernent les entrées/sorties sont exécutées de manière asynchrone

par d'autres threads: « non blocking I/O »

Slogan:

Avec Node.js, on ne fait que spécifier des réponses à des événements Et donc tout est virtuellement en parallèle.

Exemple: serveur plus évolué

(13)

 __dirname correspond au dossier où se trouve le programme javascript (voir ce post et la doc)

 La fonction readFile() génére un événement lorsque le fichier est ouvert (ou qu'une erreur survient), et la fonction en argument est appelée à ce moment.

 Le contenu du fichier est du texte. Le serveur n'a à priori pas moyen de savoir plus. Ici, on a donné comme type de contenu text/plain. Vous pouvez essayer text/html, pour voir. En général, on utilise l'extension du fichier pour lui associer un type de contenu -- mais il faut le faire faire au serveur explicitement. On peut par exemple utiliser la

librairie node-mime, mais sinon plus simplement utiliser la bibliothèque express : voir plus bas.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

varhttp = require('http'); varfs = require('fs');

//création d'un serveur web varserv = http.createServer(

//callback sur les requêtes HTTP functiontraiteRequete(req, res) { //log de l'url demandée

console.log(req.url);

//construction d'une réponse HTTP

fs.readFile(__dirname + req.url, "utf-8",

//code exécuté quand le fichier est effectivement ouvert function(err, fd) {

if(err) { // une erreur est survenue res.writeHead(500);

res.write(err.message); res.end();

} else{ // sinon, on produit le fichier voulu

res.writeHead(200, { "Content-Type": "text/plain"} ); res.write(fd);

res.end(); //envoi de la réponse }

}); });

serv.listen(8000); //commence à accepter les requêtes

(14)

26

Les bibliothèques

Node.js possède une communauté vibrante à l'origine de plus de 240000 projets, centralisée par le gestionnaire de paquets npm (Node Package Manager). Une bibliothèque Node.js s'installe avec la commande

1

npm install unebibliothequeutile

Notez que pour que cela fonctionne sur les machines du bâtiment Bréguet, il faut d'abord configurer le proxy:

1

npm config set https-proxy http://proxy.supelec.fr:8080 En standard dans Node :

 http : pour communiquer via le protocole HTTP  fs : pour interagir avec le système de fichiers  timer : pour schéduler des traitements  crypto : pour chiffrer/déchiffrer des données

Exemple de paquets accessibles le Node Package Manager (NPM) :  jquery : pour avoir accès aux facilités de jQuery

 async : helpers pour faciliter la gestion d'appels asynchrones  express : pour construire rapidement des applications web  sqlite3 : pour connecter à une base de données SQLite  mongoose : pour connecter à une base de données MongoDB  ...

Applications web avec Express

La librairie express permets de développer plus simplement des applications web. En particulier, elle permet d'associer à une "route" une fonction de callback.

Dans ce schéma, une "route" est la donnée d'un mode de requête (GET ou POST) et d'un chemin depuis la racine du site.

Installation

Pour l'installer, configurer le proxy (uniquement sur les machine du bâtiment Bréguet):

1

npm config set https-proxy http://proxy.supelec.fr:8080 puis

1

npm install express

Utilisation

On peut par exemple configurer deux routes:

1

2

3

4

5

varexpress = require('express'); varapp = express();

app.get('/accueil', function(req, res) { res.sendfile(__dirname + "/page.html");

(15)

6

7

8

9

10

11

12

13

});

app.get('/autre', function(req, res) {

res.sendfile(__dirname + "/autrepage.html"); });

app.listen(8000);

console.log("App listening on port 8000...");

Ici, à chaque "route" on associe une fonction (qui ici fait quelque chose de simple). Pour que cela fonctionne:

 Dans le répertoire du fichier javascript, placez un fichier "page.html" et un fichier "autrepage.html".

 Dans votre navigateur, essayez les addresse

1

http://localhost:8000/accueil et

1

http://localhost:8000/autre

la fonction sendfile

C'est une fonction spécifique de la librairie express, qui envoie le fichier avec le bon type mime en fonction de l'extension.

Avec des paramètres

Il est possible de récupérer les paramètres donnés dans l'url.

1

2

3

4

5

6

7

8

9

10

11

12

varexpress = require('express'); varapp = express();

app.get('/autre', function(req, res) { if("donnee"inreq.query) {

vartxt = "Donnee reçue :"+ req.query.donnee; res.send(txt); } else{ res.sendfile(__dirname+'/autrepage.html'); } });

app.listen(8000);

(16)

13

console.log('App listening on port 8000...');

Essayez l'adresse http://localhost:8000/autre?donnee=hello pour voir.

Avec express, l'attribut query de la variable req contient la liste des paires (paramètre,valeur) sous la forme d'un objet javascript: on peut donc facilement accèder aux paramètres d'une requête.

Note

Techniquement, le script au dessus est un service web ! Pas très intéressant, certe, mais néanmoins un service web. En utilisant les techniques du TD5, il est possible de faire une page html indépendante qui questionne ce service en ajax.

Utilisation de SQLite3

SQLite3 est une bibliothèque C (la version 3 du nom) qui implémente un moteur de base de données sans serveur. Une base de donnée SQLite est implémentée par un simple fichier. A l'heure actuelle, SQLite est l'un des moteurs de base de données les plus simples à installer, à manipuler et à administrer, ce qui en fait un très bon candidat pour l'utilisation dans un site web. Pour en savoir plus sur les usages de SQLite, consulter cette page. Néanmoins, le site dit lui-même:

"Le site web SQLite utilise bien-sûr lui-même SQLite et à l'heure de l'écriture de ce texte (2015) il traite entre 400.000 et 500.000 requêtes HTTP par jour, dont 15-20% sont des pages dynamiques utilisant la base de donnée. Chaque page fait en moyenne 200 requêtes SQL. Bien que la base de donnée se trouve sur une seule machine virtuelle partageant un serveur physique avec 23 autres MV, la charge moyenne reste en dessous de 0,1 la plupart du temps." Le fin mot de l'histoire est que SQLite est une bonne solution de base donnée dans le contexte d'une serveur web à traffic raisonnable.

Installation

Pour l'installer, éventuellement configurer le proxy:

1

npm config set https-proxy http://proxy.supelec.fr:8080 puis

1

npm install sqlite3

SQLite en deux mots

En bref, une base de donnée SQL est une collection de tableaux (tables) auxquels on accède grâce à leur nom. Outre son nom, un tableau est défini par ses colonnes: à chaque colonne on associe un nom et un type. Les données du tableau consistent en les lignes du tableau. Par exemple

1

2

3

4

5

$ sqlite3 test.db3 SQLite version 3.8.7.1 2014-10-29 13:59:56 Enter ".help" for usage hints.

sqlite> create table message(id INT, nom TEXT, content TEXT); sqlite> insert into message values(0,"Bob","Hello");

(17)

6

7

8

9

10

11

sqlite> insert into message values(1,"Alice","Goodbye");

sqlite> insert into message values(2,"Charlie","Good afternoon"); sqlite> select * from message; -- les commentaires sont comme ça 0|Bob|Hello

1|Alice|Goodbye

2|Charlie|Good afternoon sqlite>

Ici, on trois commandes:

 CREATE TABLE : pour créer une table. On donne le nom de la table et une liste de nom de colonnes avec leur type. Outre TEXT et INT, on peut aussi choisir FLOAT par exemple (voir la doc pour tous les choix possibles)

 INSERT INTO nomdelatable VALUES(lesvaleursseparéespardesvirgules) : pour remplir la table

 SELECT * FROM nomdelatable : pour lister le contenu de la table  Les commandes peuvent être en majuscule ou en minuscule  On peut faire des commentaires en commençant par "--"

Notez que les commandes se terminent par un point virgule (";").

La sélection peut aussi être filtrée. Par exemple, avec la table qui précède:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

sqlite> select * from message where id = 0 ; 0|Bob|Hello

sqlite> select content from message where id = 0 ; Hello

sqlite> select nom from message where id = 0 ; Bob

sqlite> select nom,content from message where id = 0 ; Bob|Hello

sqlite> select max(id) from message 2

sqlite> select * from message where id > 0 ; 1|Alice|Goodbye

2|Charlie|Good afternoon

sqlite> select * from message where nom like "%i%"; 1|Alice|Goodbye

2|Charlie|Good afternoon

sqlite> select * from message where nom like "%i%" and id > 1; 2|Charlie|Good afternoon

sqlite> select * from message where nom like "%i%" and not id > 1; 1|Alice|Goodbye

(18)

Notes:

 L'étoile ("*") dans le "select" sélectionne toutes les colonnes. On peut aussi mettre une ou plusieurs colonnes entre virgules.

 On peut aussi mettre une fonction (comme "max") pour récupérer une information sur la liste des valeurs dans une colonne.

 On peut filtrer sur des opérateurs de comparaisons sur les nombres

 On peut aussi filtrer par pattern sur un champ de texte avec "like". Le symbole "%" est remplacé par n'importe quelle séquence de caractères

 On peut combiner les assertions booléennes avec "and", mais aussi "or", et "not" Outre l'insertion et les requêtes, on peut modifier une ligne dans un tableau avec update

1

2

3

4

5

sqlite> update message set content = "Nope" where id = 0 ; sqlite> select * from message;

0|Bob|Nope 1|Alice|Goodbye

2|Charlie|Good afternoon

On peut aussi simplement supprimer une ou plusieurs lignes:

1

2

3

sqlite> delete from message where nom like "%i%"; sqlite> select * from message;

0|Bob|Nope

(ici on a supprimé deux lignes)

Finalement, on peut aussi effacer un tableau complètement avec "drop table"

1

2

3

sqlite> drop table message; sqlite> select * from message; Error: no such table: message (Argh ! Le tableau a disparu !)

La bibliothèque sqlite3 avec Node.js

Une fois installée avec npm, on peut utiliser la librairie, comme d'habitude, en créant au début du fichier javascript un objet javascript qui va contenir toutes les fonctions:

1

2

3

varsq = require('sqlite3');

sq.verbose(); // pour obtenir des informations sur l'exécution des // requêtes SQL (utile pour le débug)

On est prêt à créer une connexion sur une base de données. Pour les besoins du tutoriel, nous allons re-créer la base de donnée du dessus qui a été malencontreusement effacée. Mais on va utiliser un nom plus malin.

(19)

Donc, dans la console, dans le répertoire du fichier javascript:

1

2

3

4

5

6

7

$ sqlite3 touslesmessages.db3 SQLite version 3.8.7.1 2014-10-29 13:59:56 Enter ".help" for usage hints.

sqlite> create table message(id INT, nom TEXT, content TEXT); sqlite> insert into message values(0,"Bob","Hello");

sqlite> insert into message values(1,"Alice","Goodbye");

sqlite> insert into message values(2,"Charlie","Good afternoon");

On peut maintenant rajouter dans notre fichier javascript

1

vardb = newsq.Database(__dirname + '/touslesmessages.db3'); et on a un objet db qui est lié à la base de données.

Comment l'utiliser ? Il y a essentiellement trois méthodes utiles:  db.each("COMMANDE SQL", function(err,row) { ... })

 Exécute la commande sql

 Sur chaque ligne retournée, applique (en séquence) la fonction.

 L'objet row a pour attributs les noms de colonnes et commes valeurs... les valeurs.  db.all("COMMANDE SQL", function(err,rows) { ... })

 Pareil, mais retourne la liste des lignes et applique la fonction dessus (l'argument rows).  db.run("COMMANDE SQL")

 Pour quand on ne souhaite pas faire quelque chose après (par exemple, après une insertion ou une modification de ligne

Exemple simple

Un exemple complet (qui ne lance pas de serveur) pourrait être:

1

2

3

4

5

6

7

8

9

10

varsq = require('sqlite3'); sq.verbose();

vardb = newsq.Database(__dirname + '/touslesmessages.db3');

db.each("SELECT * FROM message", function(err, row) { if(err) {

console.log(err); } else{

(20)

11

}

});

Sauvez ce programme comme "message.js" et exécutez-le avec la commande

1

nodejs message.js Admirez le résultat.

Vous pouvez facilement imaginer comment demander au code de rajouter ou d'effacer une ligne dans la base de données.

Note

On peut maintenant évidemment transformer ce code en un service web avec express, et c'est l'objectif du TD6.

Avec des paramètres externes

La fonction run peut être utilisée avec des paramètres dans la commandes SQL: on met des "?" à la place de ce que l'on veut mettre, et donner en deuxième argument de run un

tableau. Par exemple: ?

1

db.run("INSERT INTO message VALUES(?,?,?)", [3,"David","I am here"]); va effectivement faire

?

1

db.run("INSERT INTO message VALUES(3,\"David\",\"I am here\")"); L'intérêt est que

 vous n'avez pas besoin d'échapper les caractères problématiques: javascript le fait pour vous

 la commande SQL est écrite une fois pour toute

Les autres fonctions peuvent aussi prendre des paramètres externes. Par exemple: ?

1

2

3

4

5

6

7

8

db.each("SELECT * FROM message WHERE id > ?", [2], function(err, row) {

if(err) {

console.log(err); } else{

console.log(row.nom + "("+ row.id + ") a écrit '"+ row.content + "'"); }

Références

Documents relatifs

Par ailleurs, les enfants ont tous utilisé PLAY-ON à partir du clavier, c'est à dire des flèches directionnelles (la gauche et la droite pour la discrimination et les quatre pour

Many of MDE activities such as DSL creation, model editing, or model transfor- mations involve repetitive tasks and a lot of user interactions with the user interface of the MDE

Pour répondre à notre problématique, trois séries d’hypothèses de recherche sont formulées quant à l’influence du format de présentation de la performance, de son

Il ne reste plus qu’à analyser la distribution des POS pour les mots les plus fréquents et celle des adjectifs, verbes et noms communs.. Préparation

Mais malgré la dotation à l’Égypte, par le budget saoudien, d’avions Rafale et de navires Mistral, en sus de l’aide financière annuelle des États-Unis, l’armée égyptienne

La distinction entre véracité historique et élan narratif est plus évidente encore lorsqu’il s’agit d’un programme de fantasy, comme la série animée Désenchantée

De plus en plus de preuves laissent penser que l'utilisation d'information sociale chez les animaux est chose commune. Dans un contexte de choix de partenaire,

On peut expliquer autant que l’on peut et l’on veut l’état des choses (tathāta), jamais on ne pourra en dire le pourquoi, du moins dans la condition actuelle de l’homme