1 of 31

Server Side JavaScript

Partie 2: le module Express

Michel Buffa

2 of 31

Avant tout: objets prédéfinis dans NodeJS

Souvent utilisés avec Express:

__dirname : répertoire courant

Exemple: res.sendfile(__dirname + '/index.html');�

__filename : nom du fichier qui exécute le code serveur

3 of 31

Express: HTTP, routage, Web Services, templates

Le module Express est un des plus populaires

Installer avec :

$ npm install -save express

Permet de définir facilement des “routes”, de servir des fichiers statiques, de faire des Web Services renvoyant/acceptant du JSON…

4 of 31

Premier exemple:

var express = require('express');�var app = express();�app.get('/', (req, res) => {� res.send('Hello World');�})��var server = app.listen(8085, function () {� var host = server.address().address� var port = server.address().portconsole.log("Serveur écoute sur http://%s:%s", host, port)�})

5 of 31

app.get, app.post, app.put, app.delete...

// This responds to a GET request with "Hello World" on the homepage�app.get('/', (req, res) => {� console.log("Got a GET request for the homepage");� res.send('Hello GET');�})��// This responds to a POST request for the homepage�app.post('/', (req, res) => {� console.log("Got a POST request for the homepage");� res.send('Hello POST');�})�

6 of 31

Autres exemples: support d’expressions régulières

// This responds a GET request for the /list_user page.�app.get('/list_user', (req, res) => {� console.log("Got a GET request for /list_user");� res.send('Page Listing');�})��// This responds a GET request for abcd, abxcd, ab123cd, and so on�app.get('/ab*cd', (req, res) => { � console.log("Got a GET request for /ab*cd");� res.send('Page Pattern Match');�})

7 of 31

Avec la dernière route..

8 of 31

On peut aussi faire cela...

// routing�app.get(/\/track\/(\w+)\/(?:sound|visualisation)\/((\w|.)+)/, � (req, res) => {� res.sendfile(__dirname + '/' + TRACKS_PATH + req.params[0] � + '/' + req.params[1]);�});

Matche:

  • /track/nom_track/sound/guitar.mp3
  • /track/nom_track/visualization/bass.png

9 of 31

Récupérer des bouts de chemin dans l’URL

Avec http://localhost:8085/track/HighwayToHell

app.get('/track/:id', (req, res) => {� var id = req.params.id; // vaut HighwayToHell�� // Faire quelque chose avec id, aller chercher l'objet dans une BD// etc...�});

10 of 31

Récupérer des paramètres HTTP

Avec http://localhost:8085/track/HighwayToHell?format=mp3

app.get('/track/:id', (req, res) => {� var id = req.params.id; // params pour chemin dans URL� var format = req.query.format; // query pour params HTTP

// Faire quelque chose avec id et la valeur du format,

// aller chercher l'objet dans une BD// etc...�});

11 of 31

Envoi d’un fichier statique par HTTP sans Express

http.createServer( (request, response) => { � // Parse the request containing file namevar pathname = url.parse(request.url).pathname;� � // Print the name of the file for which request is made.console.log("Request for " + pathname + " received.");� � // Read the requested file content from file system� fs.readFile(pathname.substr(1), (err, data) => {� if (err) {� console.log(err);� // HTTP Status: 404 : NOT FOUND// Content Type: text/plain� response.writeHead(404, {'Content-Type': 'text/html'});� }else{ � //Page found // HTTP Status: 200 : OK// Content Type: text/plain� response.writeHead(200, {'Content-Type': 'text/html'}); � � // Write the content of the file to response body� response.write(data.toString()); � }� // Send the response body � response.end();� }); �}).listen(8081);

12 of 31

Fichiers statiques avec Express

var express = require('express');�var app = express();�// avec cette ligne, index.html est par defaut la page d’accueil

app.use(express.static('public')); // ICI IMPORTANT !�

// Avec la règle ci-dessous on peut redéfinir la page d’accueil�app.get('/', (req, res) => { // Page d’accueil� res.sendFile(__dirname + "/public/maPage.html");�});var server = app.listen(8081, function () {})�

13 of 31

Avec l’exemple précédent

Si on a la hiérarchie suivante :�

node_modules�server.js�public/

public/index.html�public/images�public/images/logo.png�

Le serveur répondra à :

http://localhost:8081/images/logo.png

http://localhost:8081 renverra public/index.html

14 of 31

Formulaires classiques avec POST

<form action="process_post" method="POST">�First Name: <input type="text" name="first_name"> <br>�Last Name: <input type="text" name="last_name"><input type="submit" value="Submit"></form>

15 of 31

Utilisation du module body-parser

var bodyParser = require('body-parser');

// Create application/x-www-form-urlencoded parservar urlencodedParser = bodyParser.urlencoded({ extended: false })

�app.post('/process_post', urlencodedParser, (req, res) => {� // Prepare output in JSON format� response = {� first_name:req.body.first_name,� last_name:req.body.last_name� };� console.log(JSON.stringify(response));� res.end(JSON.stringify(response)); // plus tard, ça ira dans MongoDB par exemple...�})

16 of 31

Exemple de route POST pour formulaire multipart

<form action="/api/restaurants" method="post"

enctype="multipart/form-data">

Nom: <input type="text" name="nom"><br>

Cuisine: <input type="text" name="cuisine"><br>

<input type="submit" value="Submit">

</form>

17 of 31

Exemple de route POST pour formulaire multipart

// pour les formulaires multiparts

var multer = require('multer');

var multerData = multer();

app.post('/api/restaurants', multerData.fields([]), (req, res) => {

let nom = req.body.nom;

let cuisine = req.body.cuisine;

let reponse = {

msg: "J'insère un restaurant nom = " + nom + " cuisine = " + cuisine,

data: "id du restaurant inséré " + Math.random(),

err: ""

}

res.send(JSON.stringify(reponse));

}

18 of 31

Ex envoi de fichier multipart...

<input id="file" type="file" multiple/>

<script>

var fileInput = document.querySelector('#file');

fileInput.onchange = function() {

// requête Ajax vers serveur pour envoyer les fichiers sélectionnés

// …

}

</script>

19 of 31

Upload de fichiers, formulaires multiparts

Le module MULTER est celui que je vous recommande !

var multer = require("multer");�// multer configuration: destination, filename customization, etc.var storage = multer.diskStorage({� destination: "./public/uploads",� filename: function (req, file, cb) {� cb(null, file.originalname + '-' + Date.now());� }�});�// instaciation of a multer processorvar upload = multer({ storage: storage });�

20 of 31

Exemple pour de l’envoi de fichiers multiples

app.post('/api/file', upload.array('file'), (req, res) => {� console.log("received " + req.files.length + " files");// form filesfor(var i=0; i < req.files.length; i++) {� console.log("### " + req.files[i].path);� }� console.log("The URL for the file is:" +

"localhost:3000\\"+req.file.path);� res.status(204).end(); �});

21 of 31

Extrait du code JavaScript du client

fileInput.onchange = function() {� var data = new FormData();� for(var i=0; i < fileInput.files.length; i++)� data.append('file', fileInput.files[i]); // tous le même nom !fetch(url, {

method: 'post',

body: data

});� };

22 of 31

L’exemple précédent marche aussi en Ajax / Xhr2

Dans le TP vous ferez tourner cet exemple complet, avec un client Ajax / Xhr2

23 of 31

Sessions HTTP et express

var express = require('express');�var app = express();��app.use(express.cookieParser());�app.use(express.session({secret: '1234567890QWERTY'}));��app.get('/awesome', (req, res) => {� if(req.session.lastPage) {� res.write('Last page was: ' + req.session.lastPage + '. ');� }� req.session.lastPage = '/awesome';� res.send('Your Awesome.');�});

24 of 31

Sessions HTTP et express, sessions persistantes

On peut stocker la session dans un “store” externe comme Redis

On peut aussi la stocker dans MongoDB

Voir: http://blog.modulus.io/nodejs-and-express-sessions

25 of 31

Express et templates

Le plus souvent: NodeJS sert à des applications fullJS / Ajax et on fait des templates côté client, le serveur ne renvoyant que du JSON à part pour quelques pages HTML statiques (page d’accueil par exemple).

Il existe néanmoins des langages de templates: JADE, EJS, c’est pas compliqué, c’est comme les pages JSPs à peu de choses près !

Je n’aime pas trop cela… on se croirait revenu aux années 1997 ou aux débuts de PHP.

26 of 31

Exemple avec JADE

  1. Installer jade: npm install jade
  2. Créer un répertoire views dans lequel on mettra les fichiers de templates se terminant par .jade
  3. Indiquer qu’on utilise ce moteur de templates :� app.set('view engine', 'jade');

27 of 31

Exemple avec JADE, suite...

Fichier views/index.jade :

html� head� title!= title� body� h1!= message

app.get('/', (req, res) => {� res.render('index',

{

title: 'Hey',

message: 'Hello there!'

}

);�});

28 of 31

Exemple avec EJS

  • Installer jade: npm install ejs
  • Créer un répertoire views dans lequel on mettra les fichiers de templates se terminant par .ejs
  • Indiquer qu’on utilise ce moteur de templates:� app.set('view engine', ejs);

29 of 31

Exemple avec JADE, suite...

Fichier views/index.ejs :

<h1>Je vais compter jusqu'à <%= compteur %></h1>�<p><%� for(var i = 1 ; i <= compteur ; i++) {� %>� <%= i %>... �<% } %></p>�<p>Tant que j'y suis, je prends un nom au hasard qu'on m'a envoyé :�<%= noms[Math.round(Math.random() * (noms.length - 1))] %></p>

app.get('/compter/:nombre',(req, res) => {� var lesNoms = ['Robert',

'Jacques', 'David'];� res.render(index.ejs',

{

compteur: req.params.nombre,

noms: lesNoms

});�});

30 of 31

Vous en pensez quoi ?

Sérieusement….

Qui peut encore écrire du code comme ça ??????

En 1999 SUN Microsystem avait déjà changé la syntaxe des JSPs version 1, PHP avait déjà recommandé de ne plus faire de code spaghetti en mélangeant HTML et PHP…

Et je vois çà en 2021 ???

31 of 31

Bonne solution : vues côté client à 100% !

  • Le serveur renvoie du JSON
  • Côté client on injecte les données reçues dans les vues :

Utiliser le DOM “standard”

Ou via jQuery

Ou via une framework MVC moderne (backbone, angularJS, etc.) qui va faire du binding entre: web services, données JavaScript et vues