Route
Dans les chapitres précédents, nous avons vu comment
construire une réponse
du serveur aux requêtes envoyées par un client.
Cependant, mis à part pour les
fichiers statiques du dossier /public,
les serveurs que nous avons construits envoient toujours la même réponse au client, quelle que soit la requête
reçue.
La notion de route permet de différencier le traitement des requêtes en fonction de la méthode utilisée,
du chemin demandé et des données éventuelles qu'elles contiennent.
Une route définit un lien entre les propriétés d'une requête et le traitement spécifique
qui lui est associé.
La documentation officielle du
Routage Express
se trouve
ici
.
Simple GET
Les requêtes GET sont utilisées pour demander au serveur de renvoyer
une information spécifique basée sur le chemin de la requête, et éventuellement des paramètres
transmis au serveur directement dans l'URL demandée.
La méthode get(chemin, handler)
d'Express permet d'ajouter des middlewares
qui ne se déclenchent que pour des requêtes GET sur un chemin spécifique.
L'exemple ci-dessous montre comment créer une application Express qui renvoie le message
"Bienvenue sur la HOME PAGE" lorsque le serveur reçoit une requête GET sur le chemin "/".
Avec ce serveur, un accès à l'adresse
http://127.0.0.1:3000/
renvoie le message "Bienvenue sur la HOME PAGE".
Ce fonctionnement peut sembler très similaire à la
1ère mise en oeuvre d'Express
que nous avions faite dans un autre chapitre.
Il y a pourtant une grosse différence dans le sens ou ce serveur ne renvoie le message que pour une
requête GET sur le chemin "/".
Pour s'en convaincre, il suffit de tester l'accès à une autre URL comme par exemple
http://127.0.0.1:3000/truc/machin/chose :
si l'ancien serveur renvoyait le message quelle que soit l'URL, celui-ci renvoie une erreur lorsque
l'URL demandée ne correspond à aucune route.
Pour traiter d'autres chemins, il faut ajouter d'autres déclarations de routes.
NB : les chemins définis peuvent utiliser des expressions régulières.
NB : le matching sur les routes déclarées est fait séquentiellement dans l'ordre de déclaration des routes
jusqu'à ce qu'un pattern soit trouvé.
L'exemple ci-dessous ajoute 2 nouvelles routes :
Redirection
Dans un routage, au lieu de renvoyer directement une réponse, il peut être intéressant de rediriger le
navigateur client vers une autre URL.
La méthode
redirect
de l'objet res
permet d'effectuer une redirection.
Dans l'exemple ci-dessous, la route par défaut n'envoie plus un message, mais redirige automatiquement
le client vers la home page.
Exercice 04
Dans les
exemples précédents,
nous avons créé une "home page" utilisant un layout EJS et qui était renvoyée par le serveur quelle
que soit la requête reçue.
Cet exercice consiste à créer un site simple sur le même modèle et gérant 2 routes :
-
/
: la page d'accueil qui affiche "> Accueil" en haut à gauche et
"Bienvenue sur Démo Routage Express" en grand au centre.
"
-
/about
: une page qui affiche "> À propos" en haut à gauche et
"© Copyright 2022 votre_nom" en grand au centre.
-
Toute autre requête doit être redirigée vers la page d'Accueil.
Le header contient une barre de navigation avec 2 liens/boutons permettant d'accéder aux
différentes routes du site.
Le site doité être réalisé en utilisant un layout EJS lui-même utilisant des parties séparées pour
son header et son footer.
L'image à utiliser sur la page d'accueil se trouve
ici.
Design de la page "Accueil" :
Design de la page "À propos" :
Router
Dans un "réel" site web, le nombre de routes (ex. /
, /research
,
/teaching
) et sous-routes (ex. /teaching/node
, /teaching/node/express
,
/teaching/javascript
, ...)
peut être conséquent.
Les traitements correspondants alourdissent le fichier app.js
(qui contient en plus tous
les middlewares, etc.).
Exemple (simple !) :
app.js
const express = require('express');
const app = express();
// dossier public (pour le css, etc.)
const path = require('path');
app.use(express.static(path.join(__dirname, 'public')));
// définition du view engine
app.set('view engine', 'ejs'); // npm install --save ejs
app.set('views', path.join(__dirname, 'views'));
// layout
const expressLayouts = require('express-ejs-layouts'); //npm install express-ejs-layouts
app.use(expressLayouts);
app.set('layout', '../views/layouts/layout') ; // définit le layout par défaut
// ... il pourrait y a voir plus de choses ici...
// ROUTAGE (les traitements ci-dessous sont très simplifiés pour l'exemple)
app.get("/", (req, res) => {
res.send('<h1>Bienvenue chez Grégory Bourguin</h1>');
});
app.get("/research", (req, res) => {
res.send("<h1>Travaux de Recherche</h1>");
});
app.get('/teaching', (req, res) => {
res.send('<h1>Enseignements</h1>') ;
});
app.get('/teaching/javascript', (req, res) => {
res.send('<h1>Cours de JavaScript</h1>') ;
});
app.get('/teaching/php', (req, res) => {
res.send('<h1>Cours de PHP</h1>') ;
});
app.get('/teaching/node', (req, res) => {
res.send('<h1>Introduction à Node.js</h1>') ;
});
app.get('/teaching/node/express', (req, res) => {
res.send('<h1>Cours sur le framework Express</h1>') ;
});
app.get('*', (req, res) => {
res.redirect('/')
});
module.exports = app
Instructions :
- Créez un nouveau projet et faites tourner l'application ci-dessus.
- Testez les routes...
Pour pallier ce problème et séparer les traitements liés à un routage complexe,
Express introduit la notion de
Router.
L'idée générale est de créer différents routers qui sont chacun chargés de la gestion d'un ensemble de routes.
Chaque router est défini dans un fichier Javascript différent et, par convention,
les fichiers routers sont placés dans le dossier /routes
du serveur.
Identification des routers
La première étape consiste à identifier les routes "principales" qui correspondent à des routers.
Dans notre exemple, on peut identifier les routes principales suivantes :
-
/
: sera gérée par homeRouter.js
-
/research
: sera gérée par researchRouter.js
-
/teaching
: sera gérée par teachingRouter.js
La délégation du traitement de chaque route "principale" à un router est spécifiée dans le fichier
app.js
(NB : les fichiers routers seront définis dans l'étape suivante).
Vous remarquerez que chaque router est bien attaché à une route principale spécifique
(lignes 19
, 22
et 25
).
Définition des routers
Il reste à définir le contenu des routers (ce qu'on aurait du faire avant, mais il est important
de comprendre qu'un router est attaché à une route principale dans app.js
!).
homeRouter.js
Le code de homeRouter
est simple du fait qu'il ne gère qu'une seule (sous-)route correspondant
à la racine du serveur.
researchRouter.js
Le code de researchRouter
demande un peu plus de réflexion.
La route définie ligne 8
spécifie le chemin /
: il est important de bien
comprendre que dans ce contexte, /
signifie la racine du router
(et non pas celle du serveur).
Puisque dans app.js
(ligne 22
), researchRouter
est lié au
chemin /research
, le chemin /
défini dans researchRouter
(ligne 8
) correspond en fait à /research
+ /
,
c'est à dire : /research/
(en fait équivalent à
http://127.0.0.1:3000/research).
teachingRouter.js
Le code le plus complexe est celui de teachingRouter
du fait qu'il spécifie le traitement de
nombreuses sous-routes.
Comme précédemment, chaque chemin indiqué dans teachingRouter.js
, doit être compris dans le
contexte du chemin auquel ce router est lié dans app.js
(ligne 25
),
c'est à dire qu'il est concanténé à la base /teaching
.
Ainsi, par exemple, le chemin de routage /node/express
défini dans teachingRouter.js
ligne 22
, correspond en réalité à l'URL :
http://127.0.0.1:3000/teaching/node/express
.
Exercice 5
L'exemple que nous venons de développer utilise de simple send
pour répondre aux requêtes
d'un client.
Dupliquez ce serveur et transformez-le pour qu'il réponde avec de
"vraies" pages web générées en utilisant
un layout et des vues EJS.
Exercice 06
Maintenant que les routers n'ont plus de secret pour vous, dupliquez le serveur que vous avez réalisé
dans
l'Exercice 04 et transformez-le pour qu'il mette en oeuvre des routers pour ses
2 routes principales, soient /
et /about
.
NB : ces routers sont très simples, mais cela permettra de vérifier que vous êtes capables de mettre
en oeuvre cette nouvelle architecture de serveur par vous-mêmes ;).
Controller
La dernière étape pour se conformer à la convention du routage Express consiste à ajouter la
notion de controller.
Un Controller correspond à un fichier Javascript qui exporte un ensemble de fonctions
utilisées par un Router.
Le principe est d'externaliser les fonctions de traitement des routers (qui peuvent être très longues).
Par convention, les controllers sont créés dans le dossier /controllers
.
Dans notre exemple, nous créons les controllers suivants :
homeController
researchController
teachingController
Fichiers homeController.js
et homeRouter.js
(modifié) :
Fichiers researchController.js
et researchRouter.js
(modifié) :
Fichier teachingController.js
:
(vous devriez être capables de modifier teachingRouter.js
tout seuls :) )
Exercice 07
Dupliquez le serveur que vous avez réalisé dans
l'Exercice 06
et modifiez-le pour qu'il mette en oeuvre les controllers adéquats.
Le sujet se trouve
ICI.
Paramètres de Route
Un paramètre de route correspond à une partie du chemin de l'URL de la requête.
Considérons par exemple l'URL : http://127.0.0.1:3000/user/666
La partie du chemin /666 correspond à l'identifiant de l'utilisateur
(par ex. dans la BD) et le handler de requête doit utiliser cette valeur pour fournir les informations
sur le bon utilisateur.
Les paramètres sont "déclarés" en utilisant :nom_param
dans le pattern
de la route.
Pour l'exemple ci-dessus, si on veut nommer le paramètre "id",
on utilise le chemin de route /user/:id
.
La récupération des paramètres se fait simplement en accédant à l'attribut
req.params
Dans notre exemple, on trouvera donc la valeur 666
dans req.params.id
.
Et voici un exemple de traîtement :
Exercice 08
Créez l'application et la vue show_route_params
utilisée dans l'exemple
que nous venons de voir pour que le résultat soit le suivant :
Paramètres GET
Les paramètres d'une requête GET sont envoyés dans l'URL de la requête sous la forme
url?name=value[&name=value]
(name
est le nom du paramètre transmis et value
sa valeur).
Exemple : http://127.0.0.1:3000/?day=9&month=octobre&year=1971
.
Cette requête contient 3 paramètres :
day
= 9
month
= octobre
year
= 1971
La récupération des paramètres se fait simplement en accédant à l'attribut
req.query
Dans l'URL précédente, on aura par exemple req.query.day
qui vaut 9
.
Voici un exemple de traitement :
Exercice 09
Complétez l'application de
l'Exercice 08 avec le handler de traitement des
paramètres GET ci-dessus (sans modifier le render
:) )
et créez la vue show_get_params
pour que le résultat soit le suivant :
Données POST
Envoi d'un formulaire :
Les données POST proviennent généralement d'un formulaire.
Pour notre exemple, nous allons fournir une vue contenant un formulaire :
-> l'action
déclenche la route /form
en méthode POST
.
-> il y a 3 champs : firstname
, lastname
et button
.
(NB : j'ai utilisé les css Boostrap)
Cette vue peut par exemple envoyée par la route /form
en méthode GET
:
Traitement du formulaire après soumission :
Les données POST sont contenues dans le corps de la requête.
De ce fait, elles doivent être parsées ce qui rend leur récupération plus complexe.
Express fournit 2 modules permettant d'extraire automatiquement les données du corps
d'une requête POST : express.json
et express.urlencoded
.
Les données sont alors disponibles dans l'attribut req.body
.
Les modules doivent être insérés en tant que middlewares (avant les routes) :
Et voici un exemple de traitement des données reçues du formulaire :
Exercice 10
Complétez l'application de
l'Exercice 09 avec tout ce que nous venons de voir
(vue et route d'envoi du formulaire, route de traitement des données du formulaire
après soumission) et créez la vue show_post_data
pour que le résultat comme ci-dessous.
Envoi du formulaire :
Traitement du formulaire :
Le sujet se trouve
ICI.
Le sujet se trouve
ICI.