I Introduction aux modules Express et Ejs
I.1 Présentation
Le module Express permet de gérer facilement :
- le routing ;
- les sessions ;
- l’interfaçage avec un générateur de template (compatible, ici Ejs sera utilisé).
Les documentations sont accessibles aux liens suivants :
- module Express : https://expressjs.com/ et https://expressjs.com/fr/ traduit en français
- module Ejs : https://ejs.co/
I.2 Installation d’Express et d’Ejs
npm install express -save
npm install ejs -save
I.3 Hello Word
Premier exemple permettant de comprendre la structure de base
const express = require('express')
const app = express()
const port = 8080
app.get('/', (req, res) => {
res.send('Hello world!')
})
app.listen(port, () => {
console.log(`Ecoute sur http://localhost:${port} avec Express`)
})
L’objet app va gérer toutes les connexions entrantes. Ici il écoute sur le port 8080 et renverra :
- une réponse pour tout accès de type GET sur l’url path « / »
- ici « Hello world! »
- une réponse de non existance d’URL path si non définie
Exemple de retour pour une URL non gérée: de type « http://127.0.0.1/test »
Cannot GET /test
I.4 Hello Word avec template Ejs
Les templates sont stockés par défaut dans un sous répertoire nommé « views ». Le fichier « acceuil.ejs » est créé et il contiendra la code suivant :
<!DOCTYPE html>
<html>
<head>
<title>Hello</title>
</head>
<body>
<h1>Acceuil</h1>
<p> Hello World </p>
</body>
</html>
On revient dans le fichier à la racine qui sera lancé dans node.js :
const express = require('express')
const ejs = require('ejs');
// Instancie l'application express
const app = express()
// associe le moteur de template Ejs à Express
app.set('view engine', 'ejs');
const port = 8080
app.get('/', (req, res) => {
// récupère le template accueil.ejs et envoie le flux http au client
res.render('accueil');
})
app.listen(port, () => {
console.log(`Ecoute sur http://localhost:${port} avec Express`)
})
Ici l’exemple est plutôt trivial mais c’est le principe général. On remarque :
- pas besoin de déclarer le nom complet du template (accueil.ejs),
- pas besoin de fermer la connexion (res.end()). La méthode « .render() » s’en occupe
Deuxième exemple avec un template utilisant un paramètre qui contiendra « Hello Word ».
Contenu du fichier « accueil.js » :
<!DOCTYPE html>
<html>
<head>
<title>Hello</title>
</head>
<body>
<h1>Acceuil</h1>
<p> <%= texte %> </p>
</body>
</html>
Ici le code javascript est iclus dans les balise <% …%>. Et dans ce cas précis les balises <%= … %> affiche le contenu de la variable.
On revient dans le fichier à la racine qui sera lancé dans node.js :
const express = require('express')
const ejs = require('ejs');
// Instancie l'application express
const app = express()
// associe le moteur de template Ejs à Express
app.set('view engine', 'ejs');
const port = 8080
app.get('/', (req, res) => {
let texte = "Hello World ! (paramètre vers le template)"
// récupère le template accueil.ejs et envoie le flux http au client
res.render('accueil', {texte:texte});
})
app.listen(port, () => {
console.log(`Ecoute sur http://localhost:${port} avec Express`)
})
On remarque que :
- la variable texte contient bien « Hello world …’
- que cette variable est transmise au template via cette ligne javascript : res.render(‘accueil’, {texte:texte});
II Module Express
Il est composé de 5 principaux composants :
- Express : fonction la plus haut niveau.
- Retourne un objet de type « Application » pour une simple instiation
- Voir en détails en utilisant les méthodes spécifiques (peut retourner un objet routeur par ex)
- Application : c’est l’objet principal. Suivant les méthodes utilisées, et plus particulièrement les callback, les objets « Request » et « Response » sont utilisés
- Request : objet dédié à la demande
- Response : objet dédié à la réponse
- Router : utilisé pour des middlewares personnels dans le cadre du routing.
Les objets les plus utilisés seront : Application; Request et Responses
II.1 Application
C’est cet objet qui permettra de faire essentiellement du routing. il faut retenir les méthodes suivantes :
app.all() | Va traiter tous les types de requêtes HTTP (PUT, GET, DELETE,POST) ou autres. Cette méthode est pratique pour mettre en place un logique de mapping global, traiter les authentification des utilisateurs, etc. |
app.delete() | Va traiter les type de requête HTTP DELETE |
app.get() | Va traiter les type de requête HTTP GET |
app.listen() | Monte un serveur web sur un port particulier. app.listen([port[, host[, backlog]]][, callback]) |
app.post() | Va traiter les type de requête HTTP POST |
app.put() | Va traiter les type de requête HTTP PUT |
app.render() | Pemet d’utiliser un moteur de template |
app.route() | Permet d’utiliser une instance d’une simple route et d’y associe plusieurs middlewares spécifiques asociés à la route. ex : app.route(‘/events’) .all(function (req, res, next) { // runs for all HTTP verbs first // think of it as route specific middleware! }) .get(function (req, res, next) { res.json({}) }) .post(function (req, res, next) { // maybe add a new event… }) Ceci pour centraliser les réponses à faire suivant les requêtes de type GET, POST ou autres pour un même chemin. Cela évite de faire des erreurs de doublons et clarifie le code |
app.use() | Déclare des middleware spécifiques |
II.2 Request et Response
Les objets Request et Response concerne respectivement la requête web (resquest) et la réponse à faire (response).
Les méthodes/propriétés les plus utilisées de Request seront :
req.body | obtenir les clefs/valeurs des données transmisses dans le body de la requête |
req.hostname | nom du hostname |
req.ip | adresse ip |
req.method | Méthode HTTP utilisée (GET, POST, etc.) |
originalUrl, baseUrl, path | Information sur le niveau des URL. Exemple : app.use(‘/admin’, function (req, res, next) { // GET ‘http://www.example.com/admin/new’ console.dir(req.originalUrl) // ‘/admin/new’ console.dir(req.baseUrl) // ‘/admin’ console.dir(req.path) // ‘/new’ next() }) |
params | Permet de récupérer des propriétés au sein d’une URL Exemple pour un chemin déclaré de cette manière /user/:name // GET /user/tj console.dir(req.params.name) // => ‘tj’ |
query | Permet de récupérer les variables indiquées dans l’url Exemple : // GET /search?q=tobi+ferret console.dir(req.query.q) // => ‘tobi ferret’ // GET /shoes?order=desc&shoe[color]=blue&shoe[type]=converse console.dir(req.query.order) // => ‘desc’ console.dir(req.query.shoe.color) // => ‘blue’ console.dir(req.query.shoe.type) // => ‘converse’ // GET /shoes?color[]=blue&color[]=black&color[]=red console.dir(req.query.color) // => [‘blue’, ‘black’, ‘red’] |
Les objets/méthodes les plus utilisées pour l’objet Response seront :
res.send() | Envoi un flux html |
res.render() | Utilise un template (et/ou un moteur de template) |
res.set() | Définit les propriétés du header |
res.redirect() | Redirection |
res.json() | Renvoie une réponse sous forme json (header type modifié). Prend en paramètre plein de type de variables (string, tableau, etc) |
La liste n’est pas exhaustive mais elle permet de commencer à prendre en main simplement ces 2 objets.
II.3 Exemple
Ci dessous quelques exemples d’implémentation
II.3.1 Exemple simple de gestion de routes
const express = require('express')
const app = express() // Instancie l'application express
app.set('view engine', 'ejs'); // associe le moteur de template Ejs à Express
const port = 8080
// Simple gestion d'accès à la racine
app.get("/", (req,res)=>{
res.send("Bienvenue sur la page d'accueil")
})
// utilisation de route pour l'url "/admin" (gestion du GET et POST au même endroit)
app.route("/admin").get((req,res,next)=> {
res.send("Page admin en GET")
}).post ((req,res,next)=> {
res.json ("{'status' : 'ok'}")
})
// Cas des pages non présentes : utilisation de son propre middleware
// mettre à la fin obligatoirement
app.use(function(req, res, next){
res.setHeader('Content-Type', 'text/plain'); // force la sortie en texte
res.status(404).send('Page introuvable !'); // status 404 et texte accompagnant
});
app.listen(port, () => {
console.log(`Ecoute sur le port:${port} avec Express`)
})
II.3.2 Exemple de traitement d’un formulaire POST
Pour pouvoir récupérer simplement les données de POST, il faudra utiliser un middle-ware spécifique : body-parser.
Ce code récupère les informations envoyées par un formulaire puis affiche les données reçues sous forme JSON.
const express = require('express')
const path = require('path');
const bodyParser = require('body-parser');
const app = express() // Instancie l'application express
app.set('view engine', 'ejs'); // associe le moteur de template Ejs à Express
const port = 8080
// enregistrement du middleware bodyparser
app.use(bodyParser.json()); // support json encoded bodies
app.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies
// utilisation de route pour l'url "/" (gestion du GET et POST au même endroit)
app.route("/").get((req,res,next)=> {
// renvoie le contenu html du fichier /views/fomulaire.html
res.sendFile(path.join(__dirname+'/views/formulaire.html'));
}).post ((req,res,next)=> {
// renvoie sous forme json les paramètres reçus
res.json (req.body) // body est rempli par le middleware body-parser
})
// Cas des pages non présentes : utilisation de son propre middleware
// mettre à la fin obligatoirement
app.use(function(req, res, next){
res.setHeader('Content-Type', 'text/plain'); // force la sortie en texte
res.status(404).send('Page introuvable !'); // status 404 et texte accompagnant
});
app.listen(port, () => {
console.log(`Ecoute sur le port:${port} avec Express`)
})
Il est possible de ne pas passer par le middle-ware « body-parser ». lien montrant comment faire : https://www.geeksforgeeks.org/parsing-form-data-in-express-app-manually-without-body-parser/
- exemple particulièrement intérresant pour comprendre et faire soi-même un middleware
II.3.3 Exemple de traitement d’un formulaire GET
Dans ce cas, le formulaire envoie les données vers /formulaire
const express = require('express')
const path = require('path');
const app = express() // Instancie l'application express
app.set('view engine', 'ejs'); // associe le moteur de template Ejs à Express
const port = 8080
app.get("/",(req,res)=> {
// renvoie le contenu html du fichier /views/fomulaire.html
res.sendFile(path.join(__dirname+'/views/formulaire_get.html'));
})
app.get("/formulaire",(req,res)=> {
// renvoie les variables du formulaires reçues sous forme JSON
res.json(req.query)
})
// Cas des pages non présentes : utilisation de son propre middleware
// mettre à la fin obligatoirement
app.use(function(req, res, next){
res.setHeader('Content-Type', 'text/plain'); // force la sortie en texte
res.status(404).send('Page introuvable !'); // status 404 et texte accompagnant
});
app.listen(port, () => {
console.log(`Ecoute sur le port:${port} avec Express`)
})
II.3.4 Exemple de traitement d’upload d’un fichier
Pour pouvoir manipuler simplement les upload, il faut utiliser le middle-ware « express-fileupload ».
– Documentation : https://www.npmjs.com/package/express-fileupload
Une fois le formulaire lancé, coté node.js le fichier se trouveras dans l’objet req.files.
Exemple : si la balise est « <input name= »fic » type = « file »> » alors le fichier disponible sera sous node.js « req.file.fic »
Soit le formulaire suivant :
<html>
<body>
<form ref='uploadForm'
id='uploadForm'
action='http://localhost:8000/upload'
method='post'
encType="multipart/form-data">
<input type="file" name="sampleFile" />
<input type='submit' value='Upload!' />
</form>
</body>
</html>
Et le code node.js :
const express = require('express')
const path = require('path')
const fileUpload = require('express-fileupload')
const app = express() // Instancie l'application express
app.set('view engine', 'ejs'); // associe le moteur de template Ejs à Express
const port = 8080
// enregistrmement du middle-ware upload : default options
app.use(fileUpload())
app.get("/",(req,res)=> {
// renvoie le contenu html du fichier /views/fomulaire.html
res.sendFile(path.join(__dirname+'/views/formulaire_upload.html'));
})
app.post("/upload",(req,res)=> {
if (!req.files || Object.keys(req.files).length === 0) {
return res.status(400).send('No files were uploaded.');
}
// The name of the input field (i.e. "sampleFile") is used to retrieve the uploaded file
let sampleFile = req.files.sampleFile;
// Use the mv() method to place the file somewhere on your server
sampleFile.mv(path.join(__dirname,"upload/" + req.files.sampleFile.name), function(err) {
if (err)
return res.status(500).send(err);
res.send("Fichier '" + req.files.sampleFile.name +"'' uploadé");
})
})
// Cas des pages non présentes : utilisation de son propre middleware
// mettre à la fin obligatoirement
app.use(function(req, res, next){
res.setHeader('Content-Type', 'text/plain'); // force la sortie en texte
res.status(404).send('Page introuvable !'); // status 404 et texte accompagnant
});
app.listen(port, () => {
console.log(`Ecoute sur le port:${port} avec Express`)
})
Ne pas hésiter à voir les options sur le site officiel : upload fichiers multiples, limitation taille, création répertoire parent, etc…
II.4 Les middlewares
II.4.1 Présentation des middlewares communs
Comme nous l’avons pu le voir, express permet l’utilisation de middle-ware. Qu’ils soient propres à l’application (midlle-ware application ou routeur) ou qu’ils proviennent de modules tiers (middle-ware tiers).
Ci-dessous une liste des middle-ware tiers à prendre en compte :
body-parser | Permet de parser automatiquement les variables intégré dans le body d’une requête (ex :formulaires POST) | https://github.com/expressjs/body-parser |
compression | Compresse la sortie web | https://github.com/expressjs/compression |
cookie-session | Gère les cookies côté client et serveur | http://expressjs.com/en/resources/middleware/cookie-session.html |
express-debug | Pour debogguer plus facilement (ne pas utiliser en prod) | https://github.com/devoidfury/express-debug |
serv-static | Associe une route à un dossier du projet. Cette route pourra directement donner accéder à tout ou partie du dossier (ex : /public) | https://github.com/expressjs/serve-static |
helmet | Sécurise les applications en définissant certaines propriétés du header | https://github.com/helmetjs/helmet |
Ici une liste plus exaustive : https://github.com/expressjs
II.4.2 Exemple d’utilisation du middleware cookie-session
const express = require('express')
const path = require('path')
const cookieSession = require('cookie-session')
const bodyParser = require('body-parser');
const app = express() // Instancie l'application express
app.set('view engine', 'ejs'); // associe le moteur de template Ejs à Express
const port = 8080
////////////////////////
// Enregistrement Middleware
///////////////////////
// enregistrement du middleware bodyparser
app.use(bodyParser.json()); // support json encoded bodies
app.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies
// Déclaration du middle-ware session
app.use(cookieSession({
name: 'session_site',
keys: ['maphrasesecrete'],
// Cookie Options
maxAge: 24 * 60 * 60 * 1000 // 24 hours
}))
//////////////////////
// Route
////////////////////
// Accueil
app.get("/",(req,res)=> {
// sesion existante
if (req.session.auth==undefined) {
// réinitialise les valeurs
req.session.nom = ""
req.session.prenom = ""
req.session.auth = 0
req.session.nb_tentative = 0
}
// session authentifié ?
if ( req.session.auth==0) {
// non : renvoi vers formulaire d'authentification
res.redirect("/auth")
} else {
// oui : affichage page accueil personnalisée
res.render("accueil2",{"session" : req.session})
}
})
// formulaire enregistrement
app.get("/auth", (req,res)=>{
res.render("session",{"session" : req.session})
})
// deconnexion : réinitialisation des valeurs cookies
app.get("/deconnect", (req,res)=>{
req.session.nom = null
req.session.prenom = null
req.session.auth = null
req.session.nb_tentative = null
res.redirect("/")
})
// vérification envoie données formulaire
app.post("/auth", (req,res)=>{
// conditions pour être authentifié
var continuer = 1
if (req.body.nom != "nom") {continuer = 0}
if (req.body.prenom != "prenom") {continuer = 0}
// renseigne le cookie des valeurs données
req.session.nom = req.body.nom
req.session.prenom = req.body.prenom
// authentifié ?
if (continuer == 1) {
// oui
req.session.auth = 1
req.session.nb_tentative = 0
} else {
// non
req.session.auth = 0
req.session.nb_tentative +=1
}
res.redirect("/")
})
// Cas des pages non présentes : utilisation de son propre middleware
// mettre à la fin obligatoirement
app.use(function(req, res, next){
res.setHeader('Content-Type', 'text/plain'); // force la sortie en texte
res.status(404).send('Page introuvable !'); // status 404 et texte accompagnant
});
app.listen(port, () => {
console.log(`Ecoute sur le port:${port} avec Express`)
})
II.5 Les bonnes pratiques
Rien à inventer, lire celles du site officiel :
- performance : https://expressjs.com/fr/advanced/best-practice-performance.html
- sécurité : https://expressjs.com/fr/advanced/best-practice-security.html
III Module Ejs
C’est un module dédié au template. Il en existe plusieurs mais celui est assez simple à prendre en main.
- site officiel : https://ejs.co/
- page npm : https://www.npmjs.com/package/ejs
Ce module s’intègre à expressjs très simplement. Ca principale force est de pouvoir intégrer du code javascript au sein du code HTML comme ce qu’il peut être fait avec PHP.
Il intègre également des include, ce qui permet de modulariser très facilement ses templates.
Pas d’exemple ici, le site officiel étant particulièrement bien détaillé en terme d’exemples.