Serveur web avec express

Auteur :
  • François Brucker

On utilise le module express pour gérer plus élégamment notre site.

Le module node express permet une gestion efficace et apaisée des site web.

Pour installer des packages node, on peut utiliser npm (node package manager) qui est livré avec node.

npm init

Comme fait précédemment on prépare le projet en créant un fichier package.json.

  1. créez un dossier serveur_express où l'on stockera les fichiers de notre serveur.
  2. dans cde dossier, initialisez le projet en tapant la commande : npm init puis en tapant entrée à chaque question pour utiliser les réponses par défaut.
  3. ajouter la ligne "type": "module", dans le fichier de configuration package.json, juste en-dessous de la ligne 5

A la fin de cette opération, vous devriez avoir le fichier un fichier nommé package.json qui contient la configuration minimale d'un projet utilisant node :

{
  "name": "code",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

Il ne contient pour l'instant que des informations générales quant au projet. Nous allons bientôt y ajouter des modules.

Le json est un format texte pour sauvegarder des données structurée. C'est un format génial qui sert tout autant pour les fichiers de configurations que le transfert de données par le web.

Ajout d'un module

Pour cela, tapez dans un terminal placé à la racine de notre projet :

npm add --save express

N'oubliez pas --save, sinon le module sera installé mais la dépendance ne sera pas ajoutée au fichier "package.json" ce qui est tarte.

Cette commande a ajouté une dépendance à notre fichier "package.json" :

"dependencies": {
    "express": "^4.17.1"
  }

A créer un fichier package-lock.json qui contient la liste de tous les modules installés et un (gros) dossier node_modules qui contient les modules installés.

Il y a plus de 50 modules installés alors que l'on a demandé d'installer que le module express, car il a lui même des dépendances et ses dépendances d'autres dépendances, etc.

La différence entre package.json et package-lock.json est que le premier décrit les versions possibles (nous, toutes les versions ultérieures à 4.17.1) alors que package-lock.json décrit la version effectivement installée (la 4.17.1). Cette mécanique permet de mettre à jour nos modules en supprimant le dossier node_modules et le fichier package-lock.json et en tapant la commande npm install.

Sauvegarde et installation du projet

Le dossier node_modules n'est pas un module à sauver, on peut installer toutes les dépendances avec la commande npm install :

Routes

Le module express va nous permettre de créer nos routes (les urls que le serveur connaît) simplement.

Avant d'utiliser le module, penchons nous un peut sur que l'on veut :

Pour l'instant nous n'avons qu'un site front. Donc commençons par organiser le tout.

Séparation front et back

L'usage veut que le site front soit séparé des actions du serveur. Cela permet de ne pas se mélanger les fichiers. On va donc créer un dossier serveur_web/static et déplacer les fichier index.html et favicon.ico

Le projet ressemble maintenant à ça, en excluant le dossier node_modules :

.
├── index.js
└── static
    └── index.html

On a utilisé la commande unix tree (tree -I node_modules) pour réaliser le dessin ci-dessus.

Notre site en express

Modifions le fichier index.js pour que notre site fonctionne sous express :

import path from "path";

import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

import express from "express";
const app = express();

const hostname = "127.0.0.1";
const port = 3000;

app.use("/static", express.static(path.join(__dirname, "/static")));

app.get("/", (req, res) => {
  res.redirect(302, "/static/index.html");
});

app.use(function (req, res) {
  console.log("et c'est le 404 : " + req.url);

  res.statusCode = 404;
  res.setHeader("Content-Type", "text/html");

  res.end(
    '<html><head><title>la quatre cent quatre</title></head><body><img  src="https://upload.wikimedia.org/wikipedia/commons/b/b4/Peugeot_404_Champs.jpg" /></body></html>'
  );
});

app.listen(port, hostname);
console.log(`Server running at http://${hostname}:${port}/`);

Tout se passe via l'objet app, qui est le résultat de l'import de express. Chaque requête au serveur passera d'un appel de app à l'autre (dans l'ordre du fichier).

Express appelle ces bouts de codes qui interceptent une requête un middleware

Pour chaque appel de app (dans notre cas on en a 2 app.use et app.get) :

  1. on vérifie si la requête satisfait l'appel de app :
    • app.use() : reçoit toutes les requêtes
    • app.get() : ne reçoit que les requêtes de méthode get
    • app.post() : ne reçoit que les requêtes de méthode post
  2. Si la requête satisfait l'appel de app, on vérifie si elle satisfait le 1er paramètre
  3. Si la requête satisfait le 1er paramètre on exécute la méthode du second paramètre avec la requête en 1er paramètre.

Comme dit précédemment, la route /static ne doit être utilisée qu'en développement. En production, on doit avoir un serveur dédié aux fichiers statiques pour éviter tout problème de charge.

Dans notre cas l'enchaînement de route est ainsi :

  1. 1er app.use : si l'url de la requête commence par /static on envoie cette requête dans le gestionnaire de fichiers statiques de express.
  2. app.get : si l'url de la requête est / on redirige la requête vers /static/index.html. Cette requête redirigée sera alors consommée par le app.use
  3. 2ème app.use : s on arrive là, c'est que toutes les routes précédentes ont échouées : on ne peut rien faire de la requêtes on indique au client que c'est un 404. On en a aussi profité pour rendre du contenu (toute une famille).

Le dernier appel de app doit être pour gérer les requêtes qui n'ont pas pu être consommées avant. C'est souvent que l'on ne sait pas quoi en faire donc on l'indique au client part un 404.

Pour plus d'informations sur le routage express, n'hésitez pas à consulter la documentation

Fonction next()

On peut remettre des requêtes utilisées en fonction avec la méthode next()

Ajoutez par exemple ce code en début de fichier en faisant en sorte que ce soit le 1er appel à app :

// ...

app.use(function (req, res, next) {
  let date = new Date(Date.now());
  console.log(
    "Time:",
    date.toLocaleDateString(),
    date.toLocaleTimeString(),
    "; url :",
    req.url
  );
  next(); // sans cette ligne on ne pourra pas poursuivre.
});

// ...

Notez Le format de la fonction change, remarquez qu'il y a un troisième paramètre, next. Lorsque vous voulez utiliser next il faut que vous l'ajoutiez en paramètre de la fonction.

Toutes les requêtes satisfont cet appel, c'est un loggeur rudimentaire.

Si vous supprimez la ligne avec next() plus rien ne fonctionne. Toutes les requêtes sont consommées.