Ces slides ont été fait avec R studio et revealjs. Revealjs est framework de présentation basé sur du JavaScript !
Hormis le côté fun de RevealJS, plusieurs fonctionnalités vous seront utiles lors des exercices :
Attention le projet web ne fait pas partie de cette UE.
Voir Fiche UE Projet Web
Supports principaux :
Tous ces supports sont accessibles et regroupés sur MooVin
Autres supports très utiles !
Pour ces raisons, investissez-vous dans ce cours maintenant ! Terminez les exercices qui sont prévus durant la semaine. Tout retard sera difficile à rattraper car la matière avance chaque semaine.
Pour ces raisons, investissez-vous dans ce cours maintenant ! Terminez les exercices qui sont prévus durant la semaine. Tout retard sera difficile à rattraper car la matière avance chaque semaine.
Systèmes d’exploitation possibles : Windows, Linux, MacOS
L’examen se déroulera sur les machines de l’école sous l’environnement Windows.
Editeur recommandé : Visual Studio Code avec extensions
TRES IMPORTANT : activer Autosave dans Vscode (voir image slide suivant)
Navigateur recommandé : Firefox
Vous pouvez travailler pour ce cours :
Sur les machines de l’école, tout est déjà installé.
Si vous souhaitez travailler sur votre propre machine, consultez les slides suivants sur les installations nécessaires
Où enregistrer vos fichiers ?
Node.js LTS :
Il est important de bien choisir la version stable de Node.js à savoir la version LTS (Long Time Support) 64 bits !!!
sudo apt install nodejs && sudo apt install npm
Laissez les choix par défaut si des questions (case à cocher, …) sont posées pendant l’installation.
Testez votre installation Node.js dans une console/terminal :
node -v
Visual Studio Code :
sudo snap install --classic code
Sous Windows, cochez “Ajouter VS code” au menu contextuel lors de l’installation
Firefox/Chrome/Safari
Nous vous conseillons vivement d’utiliser Firefox comme
navigateur !
En effet, Firefox est le plus respectueux des
normes. Donc ce qui fonctionne sous Firefox a de fortes chances de
fonctionner sur un autre navigateur, l’inverse n’est pas vrai.
Extensions à installer sous VsCode :
Compilé
Interprété
Le code JavaScript(JS) se compose de :
Les instructions sont normalement séparées par des “;”
Les “;” ne sont pas obligatoires en JS mais vivement conseillés
console.log vous sera très utile en JS notamment pour le débogage. Elle permet d’afficher un message ou encore le contenu d’une variable dans la console.
Nous avons présenté ici les différentes possibilités pour déclarer ou non les variables.
Dans le cadre de ce cours, nous nous obligerons à respecter la convention suivante :
Rappel : JS est un langage dynamiquement typé !
let students = ["studentInfo", "studentDiet"];
let person = { name: "Choquet", firstname : "olivier"};
// simple declaration of strings
const welcome = "Hello";
const world = "World";
// string concats
const concat = welcome + " " + world +" !";
// string type have many functions
const maj = welcome.toUpperCase();
// string interpolation -> use backstick (`)
const number = 7;
const message = `The number is ${number}`;
1 === 1; // true
"1" === 1; // false
1 == 1; // true
"1" == 1; // true
0 == false; // true
0 == null; // false
let object1 = { key: "value" },
object2 = { key: "value" };
object1 == object2; //false
Préférons !== et === quand c’est possible pour éviter les pièges du dynamiquement typé !
Plus de détails : MDN Network
// create objects without class -- LITTERAL NOTATION
// theses objects are simply a collection of properties
let vegetable = { name : "carrot", color: "orange" };
let vegetable = { name : "carrot", color: "orange" };
// show/access field name
const nameofvegetable = vegetable.name;
let vegetables = ["onion","garlic"];
vegetables.push("carrot"); // array vegetables now : ["onion", "garlic", "carrot"]
Toujours créer un tableau(même vide) avec l’opérateur [] !
npm install <module>
MPA signifie Multi-Page Application par opposition aux SPA (Single Page Application) que vous verrez en Bloc 2. Une application MPA renverra(“render”) à chaque requête une nouvelle page/vue.
Remarque : Node.js n’est pas qu’un serveur web mais dans le cadre de ce cours nous nous limiterons à cet aspect.
On peut consulter la requête HTTP via les outils de développement présents dans les navigateurs via l’onglet Réseau.
Dans Visual Studio Code, ajoutez la fenêtre “Terminal” (Terminal -> New Terminal).
Lancez votre application via :
npm start
Ensuite allez sur l’adresse : http://localhost:3000 via votre navigateur
Pour arrêter votre application dans Node.js -> CTRL-C
Tester votre application via :
(Ceci n’est pas obligatoire mais peut vous aider à déboguer)
npm run test
Site Express
Express est un framework facilitant le développement d’application
sous Node.js. Express se charge de récupérer et vous présenter les
requêtes HTTP sous un format simple. Grâce à Express, vous ne devez écrire
que le code de gestion(App-Specific Middleware) de votre site.
Nous verrons les répertoires et fichiers d’Express au fur et à mesure des semaines.
Cette semaine, nous nous intéressons à :
// index.js in routes directory
const express = require('express');
const router = express.Router();
// when i receive a request GET on /
// '/' is the root of the siteWeb -> lhttp://localhost:3000
router.get('/', (req, res) => {
// send a response with render -> response in HBS
// res.render search a file named index.hbs in views dir !
res.render('index.hbs');
});
// when i receive a request GET on /brol
// http://localhost:3000/brol
router.get('/brol', (req, res) => {
// send a response with render -> response in HBS
// res.render search a file named brol.hbs in views dir !
res.render('brol.hbs');
});
module.exports = router;
<!-- index.hbs code -->
<section>
<h1>Bienvenue sur le site d'apprentissage du Javascript et dédié aux ExoPlanètes.</h1>
<h3>Qu'est-ce qu'une exoplanète?</h3>
<p>
Une exoplanète est une planète située en-dehors du Système solaire, autrement dit :
elle orbite autour d'une étoile autre que le Soleil. <br>
La plupart des exoplanètes découvertes à ce jour sont situées à moins de 400 années-lumière de notre Système
solaire.
Une année-lumière équivaut approximativement à 9.460 milliards de km.
</p>
<img src="images/exoplanet.jpg" alt="illustration exoplanète" class="img" />
</section>
router.get('/', (req, res) => {
// send a response with render -> response in HBS
res.render('index.hbs');
});
Donc dans le code ci-dessus res.render va afficher layout.hbs avec {{{body}}} qui sera index.hbs
Variable HBS simple :
Variable HBS tableau/liste :
router.get('/', (req, res) => {
const tableOfVegetables = ["tomato", "banana"];
res.render('index.hbs', { simpleStringVar : "handlebars", vegetablesList : tableOfVegetables });
});
<section>
<p> Bonjour {{simpleStringVar}} </p>
<ul>
{{#each vegetablesList}}
<li> {{this}} </li>
{{/each}}
</ul>
</section>
Pour éviter une erreur de syntaxe, je vous conseille de taper each dans votre fichier vue. VsCode vous aidera à créer le bloc #each
// for ... of pour les tableaux
let vegetables = ["onion","garlic"];
for (let vege of vegetables)
{
console.log(vege);
}
// for ... in pour les objets JSON
const student = { name: 'Monica', age: 12 }
for (let key in student ) {
// display the properties
console.log(key + " => " + student[key]);
}
// expected output:
// name => Monica
// age => 12
Parcours d’un tableau d’objets …
// for traditionnel si besoin d'un index
for (let index = 0; index < 5; index++) {
console.log(index); // 0 1 2 3 4
}
// while existe aussi
let vegetables = ["onion","garlic"];
let i = 0;
while(i<vegetables.length)
{
console.log(vegetables[i]);
i++;
}
Utilisons de préférence le for … in et for … of !
En JS, il est possible (et c’est utilisé fréquemment) d’assigner une fonction comme valeur de variable.
En JS, les fonctions ne doivent pas avoir nécessairement un nom. Ceci est également fortement utilisé !
Les fonctions fléchées sont souvent anonymes et ont une syntaxe plus courte.
const welcome2 = (message) => {
return "Message : " + message;
};
let myMessage = welcome2("Hello world...");
console.log(myMessage); // Message : Hello world...
// OTHER EXAMPLE
const higher = n => n + 1;
console.log(higher(1)); // 2
C’est ce type de fonction que vous rencontrerez le plus souvent et que l’on vous demandera de réaliser !
Les paramètres lors d’un GET sont présent dans l’URL. Ils peuvent être placés directement comme ceci : http://localhost:3000?id_student=45
Ils peuvent être placés également comme ceci :
<form method="get" action="/">
<input class="form-control" type="text" name="id_student" />
<input type="submit">
</form>
Dans les 2 cas, on récupère le(s) paramètre(s) :
Le but dans une application MPA est de renvoyer une réponse à une requête. La réponse sera renvoyée via l’une des 4 instructions suivantes :
En gras, les actions les plus courantes pour une MPA.
res.render renverra une vue c’est-à-dire un fichier hbs présent dans le répertoire views. La fonction peut prendre en argument des paramètres.
res.redirect renverra vers une route.
Attention la route à fournir se fait toujours à partir de la racine du site !!!
res.render | res.redirect |
---|---|
renvoie une vue | redirige vers une route |
chemin vers le fichier hbs à partir du répertoire views | route à partir de la racine (localhost:3000) |
ne commence pas par un / | commence par un / |
on peut passer des paramètres à la vue directement | le passage de paramètre doit se faire via la route et est limité ! |
Ex : res.render(“students/index.hbs”, { studentList }); | Ex: res.redirect(“/students?id=7”); |
#if : si la condition est vraie le bloc est affiché, sinon c’est le bloc du else, s’il y en a un, qui sera affiché.
{{#if isAuthenticated}}
<a class="nav-item nav-link" href="/">Home</a>
<a class="nav-item nav-link" href="/list">List users</a>
<a class="nav-item nav-link" href="/logout">Logout</a>
<a class="nav-item nav-link" href="/">{{user}}</a>
{{else}}
<a class="nav-item nav-link" href="/">Home</a>
<a class="nav-item nav-link" href="/register">Register</a>
<a class="nav-item nav-link" href="/login">Login</a>
{{/if}}
Dans vscode, vous pouvez tapez “if” dans un fichier hbs et vscode vous aidera à créer un bloc if handlebars.
La condition utilisée dans un #if peut être une variable non booléenne (string, tableau, variable null ou undefined). Dans ce cas une string vide, un tableau vide ou une variable nulle ou undefined seront considérés comme faux.
#unless : si la condition est fausse le bloc est affiché, sinon c’est le bloc du else, s’il y en a un, qui sera affiché.
{{#unless isAuthenticated}}
<a class="nav-item nav-link" href="/">Home</a>
<a class="nav-item nav-link" href="/register">Register</a>
<a class="nav-item nav-link" href="/login">Login</a>
{{else}}
<a class="nav-item nav-link" href="/">Home</a>
<a class="nav-item nav-link" href="/list">List users</a>
<a class="nav-item nav-link" href="/logout">Logout</a>
<a class="nav-item nav-link" href="/">{{user}}</a>
{{/unless}}
unless est donc un if inversé !
La condition utilisée dans un #unless peut être une variable non booléenne (string, tableau, variable null ou undefined). Dans ce cas une string vide, un tableau vide ou une variable nulle ou undefined seront considérés comme faux.
#each : itération pour afficher les éléments d’un tableau
<ul class="list-group list-group-horizontal-lg">
{{#each userList}}
<li class="list-group-item">{{this}}</li>
{{/each}}
</ul>
Dans vscode, vous pouvez tapez “each” dans un fichier hbs et vscode vous aidera à créer un bloc each handlebars.
Ceci ne peut fonctionner qu’avec une variable de type liste /tableau !
#each : itération pour afficher les éléments d’un tableau avec alias
<ul class="list-group list-group-horizontal-lg">
{{#each userList as |userItem|}}
<li class="list-group-item">{{userItem}}</li>
{{/each}}
</ul>
Remarquez l’alias créé via as |userItem| et l’emploi de celui-ci {{userItem}}.
eq permet de faire un test d’égalité dans une vue handlebars.
<section>
<p>
{{#if (eq user.email "olivier.choquet@vinci.be") }}
Bonjour Olivier !
{{/if}}
</p>
</section>
les parenthèses autour de l’instruction eq sont nécessaires !
L’instruction eq ne fait pas partie des instructions standard du langage hbs. Il s’agit d’un ajout des professeurs de Web1.
#exists permet de tester si une variable est définie ou pas.
{{#exists var}}
<p> la variable est bien définie </p>
{{else}}
<p> la variable est undefined </p>
{{/exists}}
L’instruction #exists ne fait pas partie des instructions standard du langage hbs. Il s’agit d’un ajout des professeurs de Web1.
Model - View - Controller
Voici comment le paradigme MVC est appliqué avec Express :
Voir explications sur le slide précédent !
Un routeur est un fichier .js présent dans le dossier routes.
Il commence toujours par les 2 lignes suivantes :
et se termine toujours par la ligne suivante :
// file app.js
const studentsRouter = require('./routes/students.js’);
...
// route for this router begin with /students
app.use('/students', studentsRouter);
Il est important d’ajouter ces lignes au bon endroit !!!
Créer toujours votre variable routeur en dessous de celui déjà présent - studentsRouter dans l’exemple ci-dessus.
Faites toujours l’activation du routeur en dessous des activations déjà présentes - app.use(‘/students’, studentsRouter) dans l’exemple ci-dessus.
On crée donc un routeur dans le but de rassembler des routes ayant qqch en commun. Souvent il s’agit d’un objet.
Par exemple pour un site de gestion d’une école, je vais créer un routeur students (/students) pour gérer les utilisateurs étudiants de mon site. Ce routeur contiendra des routes pour ajouter(/add), supprimer(/delete), rechercher(/search) un étudiant et une route pour voir son bulletin de notes (/notes).
Je vais également créer un routeur teachers (/teachers) pour gérer les professeurs de mon site. Ce routeur contiendra des routes pour ajouter(/add), supprimer(/delete), rechercher(/search) un professeur et encoder une note pour ses étudiants (/addnotes).
A l’intérieur d’un routeur, les routes commencent automatiquement déjà par la route renseignée.
// file routes/students.js
const express = require('express');
const router = express.Router();
/* GET /students --> already /students because in studentsRouter */
router.get('/', function(req, res) {
...
});
/* GET /students/search --> already /students because in studentsRouter */
router.get('/search', function(req, res) {
...
});
module.exports = router;
L’instruction redirect redirige vers une route toujours depuis la racine
// file routes/students.js
const express = require('express');
const router = express.Router();
router.get('/', function(req, res) {
// KO
// redirect to root racine of the website, not redirect to /students
res.redirect('/');
});
router.post('/add', function(req, res) {
// OK
// redirect to /students
res.redirect('/students')
});
module.exports = router;
Concernant les vues, celles-ci seront comme vu jusqu’ici des fichiers handlebars (.hbs) présents dans le dossier views.
Les données seront gérées par des modèles présents dans le dossier models. Il s’agit de fichiers .js regroupant les données et les opérations sur celles-ci.
Au début les modèles contiendront des données statiques (Ex: tableau d’objets en mémoire). Ensuite les modèles contiendront des opérations ayant pour but d’aller rechercher/insérer les données dans une base de données.
//file models/User.js
// the user model is simply a collection of objects users
let usersList = [ { email : "laurent.leleux@vinci.be", pseudo : "lle"},
{ email : "olivier.choquet@vinci.be", pseudo : "och"}];
// function list
// Pay attention to modules.export to give access
// to list function outside (in controller for example)
module.exports.list = () => {
return usersList;
};
module.exports.add = (email, pseudo) => {
// data must be an object like the other in the table
// something like this { email:"truc@vinci.be", pseudo:"truc"}
const user = {email: email, pseudo: pseudo}
usersList.push(user);
};
Les modèles seront utilisés dans les routeurs :
Node.js permet l’installation de modules. Cela permet d’utiliser le code d’autres développeurs ou encore de la communauté. L’objectif de ces modules est de faciliter la réutilisation du code et ainsi d’accélérer le développement d’applications.
Au fil des séances, nous utiliserons de plus en plus de modules.
Voyons maintenant comment cela fonctionne !
Création d’un projet Node.js :
npm init
Dans le cadre de ce cours, nous vous donnerons toujours un projet de base donc la commande npm init ne sera pas utilisée.
Installation d’un module :
npm install <nom_module>
Installation d’un module uniquement pour le développement :
npm install <nom_module> --save-dev
Dans les 2 lignes de commandes précédentes <nom_module> est à remplacer par le nom d’un module node.js.
Tous les installations de modules du projet sont reprises dans le fichier package.json
Quand vous récupérez un projet Node.js (les solutions du prof via Git par ex.), il est important d’exécuter la commandenpm install
afin que les modules nécessaires au projet soient installés suivant son fichier package.json.
Le format JSON(JavaScript Object Notation) correspond simplement à la notation littérale des objets en JS. Ce format est très utilisé car simple et très compréhensible.
package.json l’utilise notamment pour décrire les modules constituant un projet.
Le module Nodemon est très utile pour recharger automatiquement le projet dans Node.js dès qu’une modification a été faite. Sans cela, vous devez à chaque fois arrêter et relancer votre application.
npm install nodemon --save-dev
nodemon app.js
Remarquez le - -save-dev pour installer nodemon uniquement pour l’environnement de développement. C’est en effet un module uniquement utile pour les développeurs, pas pour la production.
nodemon a déjà été renseigné dans le package.json par les professeurs. Allez jeter un coup d’oeil ! Remarquez également que le script npm start lance “nodemon app.js”
Express est un framework facilitant le développement d’application sous Node.js.
npm install express
node app.js
Express a déjà été renseigné dans le package.json par les professeurs. Allez jeter un coup d’oeil !
La majorité des sites Web stockent leurs informations dans une base de données. Divers types et variétés de base de données existent.
Dans le cadre de cours, nous utiliserons une base de données SQL à savoir SQLite.
Les slides suivants montrent comment créer une base de données SQLite et la gérer avec le logiciel Datagrip. Ensuite, nous verrons comment utiliser cette base de données avec une application Express.
Pour créer une base de données SQLite : New -> Data Source -> SQLite
Sur l’écran suivant, appuyez sur le +
Sur l’écran suivant, donnez un nom à votre base de données(FileName) et choisissez l’endroit où l’enregister. Consultez le slide suivant pour savoir où enregistrer correctement votre DB !
TRES IMPORTANT !!!
Où enregistrer votre Base de données SQLite ?
Pour ouvrir une base de donnée : File -> Open File -> aller rechercher votre fichier db.
Dans l’onglet avancé -> ajoutez la gestion des contraintes des clés étrangères !
La console Datagrip permet d’interagir avec la base de données SQL via des instructions SQL. Elle permet notamment :
Voici un exemple de création d’une table professeur et d’une table cours
CREATE TABLE teachers (
-- autoincrement allow to have an id incremented automatically by the db engine
id_teacher INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(255) NOT NULL,
firstname VARCHAR(255) NOT NULL,
-- active is a boolean. By default a teacher is active (1)
active BOOLEAN NOT NULL DEFAULT 1
)
CREATE TABLE courses (
-- autoincrement allow to have an id incremented automatically by the db engine
id_course INTEGER PRIMARY KEY AUTOINCREMENT,
title VARCHAR(255) NOT NULL,
number_of_hours INTEGER NOT NULL,
-- only one teacher have the responsability of the course
teacher_manager INTEGER NOT NULL,
-- FOREIGN KEY TO TABLE teachers
FOREIGN KEY(teacher_manager) REFERENCES teachers(id_teacher)
)
Dans une application Express MVC, la base de données SQL fera partie du modèle.
Pour réaliser ceci, les points suivants sont nécessaires :
Ces points sont détaillés ci-après.
npm install better-sqlite3
Remarques importantes si vous travaillez sous Mac OS :
xcode-select --install
sudo npm install node-gyp -g
Pour envoyer une requête SQL à un SGBDR(Système de Gestion de Base de Données Relationnel), on envoie une string avec les ordres SQL.
Mais quid des paramètres ?
On peut concaténer les paramètres dans la string mais cela pose plusieurs soucis :
Pour éviter ces soucis, la majorité des langages de programmation propose des Prepared Statements.
Dans ce cas, le query se fera en 2 temps :
Dans les 2 cas, on fera un prepare avant l’une de ces 3 instructions. Voir exemple slide suivant
const stmt_all = db.prepare("SELECT * FROM users");
// all() -> return alls rows in a table of objects like below
// [ {id:1, name:'user1', pseudo:'oli'}, {id:2, name:'user2', pseudo:'stef'}]
return stmt_all.all();
// ? is a parameter, the value of this parameter will be give by the call of get
const stmt_one = db.prepare("SELECT * FROM users u WHERE u.id=?");
// get(2) -> return one row where u.id=2 in an object like below
// {id:2, name:'user2', pseudo:'stef'}
return stmt_one.get(2);
// two parameters (?) for the insert
const stmt_insert = db.prepare('INSERT INTO users (name, pseudo) VALUES (?, ?)');
//run -> return infos about changes made
const info = stmt_insert.run(name, pseudo);
// file User.js
const db = require('../models/db_conf.js');
module.exports.list = () => {
// use of prepared statement
const stmt = db.prepare("SELECT * FROM USERS");
// all() -> return alls rows like [ {name:'user1', pseudo:'oli'}, {name:'user2', pseudo:'stef'}]
return stmt.all();
};
module.exports.save = (name, pseudo) => {
// use of prepared statement with parameters
const stmt = db.prepare('INSERT INTO USERS (name, pseudo) VALUES (?, ?)');
const info = stmt.run(name, pseudo);
console.log("users model save" + info.changes);
};
Nous allons ici revoir le code généré par le framework Express afin de mieux comprendre son fonctionnement.
Cette compréhension facilitera également le débogage.
Voici quelques éléments qui vous permettront d’améliorer votre technique de débogage
Le travail d’un développeur ne consiste pas seulement à réaliser un code fonctionnel. Son code doit également être clair et bien structuré pour faciliter la maintenance de l’application.
Que signifie avoir un code bien structuré ?
Cela signifie notamment que :
npm test peut vous aider dans cette tâche.
Dans Visual Studio Code, vous avez dans menu Affichage la palette de commandes et vous pouvez rechercher “Format” pour indenter correctement votre code.
Le protocole HTTP est sans état (stateless) ce qui veut dire que chaque requête est indépendante l’une de l’autre.
Comment faire alors pour conserver le fait qu’un utilisateur soit connecté et puisse accéder à différentes pages sur un site MPA sans devoir lui redemander son login/mdp à chaque page?
Les sessions résolvent ce souci. Le serveur Web peut créer une session pour chaque nouvel utilisateur. L’id de cette session est ensuite stocké chez le client sous forme de cookie et réenvoyé avec chaque requête ce qui permet de suivre l’utilisateur.
Express dispose d’un module pour utiliser les sessions.
npm install express-session
Configurer le module express-sessions : création d’une variable globale session
//app.js
const createError = require('http-errors');
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
// Use of sessions
// YOU MUST ADD THIS LINE BEFORE YOURS ROUTERS !
const session = require('express-session');
const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');
const app = express();
...
Configurer le module express-sessions : définir quelques paramètres obligatoires
Laissez ces paramètres comme dans le code ci-dessous
...
app.use(express.static(path.join(__dirname, 'public')));
// use of sessions
// YOU MUST ADD THESES LINES BEFORE APP.USE ROUTERS !
app.use(session({secret: "Your secret key", resave: false, saveUninitialized:false}));
// use of session variables in views via res.locals
app.use(function (req, res, next) {
res.locals.session = req.session;
next();
});
app.use('/', indexRouter);
app.use('/users', usersRouter);
...
Créer/récupérer une variable dans la session : req.session.”variablename”
Détruire la session
Utiliser une variable de session dans les vues grâce à res.locals
Installation module bcrypt :
npm install bcrypt
Création d’une empreinte d’une donnée :
Pour vérifier une donnée chiffrée (mot de passe par ex.), bcrypt dispose d’une méthode de comparaison du hash avec un texte en clair.
Quand vous utilisez des instructions handlebars, un contexte de base est associé.
Dans ce contexte de base se trouve toutes les variables que vous avez données à la méthode render. Cependant certaines instructions handlebars modifie ce contexte de base.
L’instruction #each modifie ce contexte de base, le contexte devient un élément du tableau. Vous n’avez donc plus accès aux variables du contexte de base dans un #each.
Pour accéder à une variable du contexte de base à l’intérieur d’un #each, utilisez @root ou ../ devant le nom de votre variable
Exemple :
Il est fort fréquent qu’un site Web permette l’envoi de documents(image, PDF, …) . Il existe en HTML l’input suivant pour réaliser ceci :
<input type="file">
Cependant il sera nécessaire de modifier le formulaire en ajoutant le support pour l’envoi de document (enctype) :
<form method="post" action="/users/add" enctype="multipart/form-data">
Le document sera envoyé sur le serveur dans un dossier à définir sur le serveur et son nom sera aléatoire. Cependant nous aurons accès à des informations sur le fichier tel que son nom original.
Nous utiliserons le module Multer pour le traitement des documents.
Installation de Multer :
npm install multer
Pour utiliser Multer, il faut lui indiquer où stocker les fichiers envoyés par l’utilisateur et quels noms doivent avoir ces fichiers. Ceci se fait via les 2 paramètres destination et filename.
Configuration de Multer :
// code to placed in routers before routes
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public/images');
},
filename: function (req, file, cb) {
const date = new Date();
const uniquePrefix = date.getFullYear() + '-' + (date.getMonth() + 1) +
'-' + date.getDate() + '-' + date.getHours() + '-' + date.getMinutes() +
'-' + date.getSeconds();
cb(null, uniquePrefix + '-' + file.originalname);
}
})
const upload = multer({ storage: storage });
router.get('/', function (req, res, next) {
res.render('index.hbs');
});
Récupération du fichier envoyé :
...
// avatar is the name of the input file !!!
// call of the single function of multer -> upload.single()
router.post('/add', upload.single('avatar'),
(req, res, next) => {
// req.file multer infos
console.log("req.file : " + JSON.stringify(req.file));
// req.file.filename ex : 2023-03-04-10-05-02-masuperimage.png
User.save({ pseudo: req.body.pseudo, image: req.file.filename });
res.redirect('/users');
});
Il est important de valider les inputs reçus par l’utilisateur afin notamment de :
Dans le cadre de ce cours, nous utiliserons le module validator pour cette tâche
Installation du module validator :
npm install validator
Les validations se font en 2 étapes :
Le module validator dispose de toute une série de fonctions intéressantes pour la validation des inputs :
Quand vous détectez une erreur, il faut ensuite l’afficher dans une vue. Plusieurs cas peuvent alors se présenter :
Voir exemples ci-après.
<form action="/adduser" method ="post">
<input type="text" name="username">
<input type="submit">
</form>
const validator = require('validator');
router.get('/users', function (req, res) {
// retrieve errors message via req.query
res.render('users/index.hbs', { errors : req.query.errors});
});
router.post('/adduser', function (req, res) {
// validate name of user -> betweeen 5 and 10 character
if (!validator.isEmail(req.body.username) {
// errors sent via query string
res.redirect('/users?errors=L\'email n\'est pas correct');
}
else {
...
}
});
<form action="/adduser" method ="post">
<input type="text" name="username">
<input type="submit">
</form>
const validator = require('validator');
router.get('/users', function (req, res) {
// retrieve errors via session variable
res.render('users/index.hbs', { errors : req.session.errors});
// destroy errors variable to avoid continous showing
req.session.errors = null;
});
router.post('/adduser', function (req, res) {
// validate name of user -> betweeen 5 and 10 character
if (!validator.isEmail(req.body.username) {
// errors placed in session variable
req.sessions.errors = "L'email n'est pas correct";
res.redirect('/users');
}
else {
...
}
});
Attention le projet Web a son UE propre -> Note séparée projet et Web 1 !
Chaque vendredi, les solutions des exercices de la semaine écoulée seront disponibles.
Consultation en ligne : Dépôt Git
Outil pour le développement
Avant de pouvoir récupérer les solutions sur votre machine, il faut :
Voir en image la procédure avec les slides ci-après.
Git est un système de contrôle de version distribué gratuit et open source conçu pour tout gérer, des petits aux très grands projets, avec rapidité et efficacité.
OK mais en clair ?
Git permet notamment de :
Dans le cadre de ce cours, l’utilisation de Git sera limitée à la récupération des solutions des exercices.
Si vous travaillez sur une machine de l’école, Git est déjà installé.
Par contre, si vous travaillez sur votre propre machine il faudra installer Git.
Pour récupérer les solutions :
git clone https://gitlab.vinci.be/olivier.choquet/web1_solutions.git
git pull origin main
npm install