Application web
Introduction
Si Node convient tout à fait à la réalisation de programmes en ligne de commande, son utilisation majoritaire est la création de serveurs d’application web.
Dans ce chapitre, nous allons nous intéresser à Express (http://expressjs.com/), un framework web minimaliste, simple mais néanmoins extrêmement puissant. En premier lieu, nous examinerons le squelette d’application qui est généré pour créer rapidement un programme qui fonctionne. C’est ensuite que nous analyserons comment tout cela est architecturé, ce qui nous permettra d’étudier une requête et sa réponse. Puis, nous illustrerons avec un exemple de serveur web qui sera capable de servir des fichiers statiques. Enfin, l’étude du routage sera le dernier point abordé, afin de connecter l’application à plusieurs middlewares différents.
Générateur Express
Avant de rentrer dans les fonctionnalités d’Express, il est important d’évoquer l’existence du programme express-generator qui se propose de générer le squelette d’une application web basée sur Express.
Ce programme est distribué via le paquet du même nom (http://npmjs.com/package/express-generator) et est installable via la ligne de commande habituelle :
$ npm install --global express-generator
Pour vérifier que le programme est correctement installé, on peut exécuter l’instruction suivante qui présente également les différents paramètres possibles :
$ express -h
Usage: express [options] [dir]
Options:
-h, --help output usage information
-V, --version output the version number
-e, --ejs add ejs engine support (defaults to jade)
--hbs add handlebars engine support
-H, --hogan add hogan.js engine support
-c, --css <engine> add stylesheet <engine> support
(less|stylus|compass) (defaults to plain css)
...
Application
Avant toute chose, il est nécessaire d’installer la bibliothèque express :
$ npm install --save express
Il est donc maintenant possible d’importer le module et de créer une instance d’Express qui représentera l’application :
// Import du module express.
var express = require('express');
// Création de l'instance de l'application.
var app = express();
Une fois l’application instanciée, elle peut être associée à un serveur HTTP afin de gérer les requêtes entrantes :
// Utilisation du module standard http pour créer le serveur HTTP.
var http = require('http');
// Création du serveur et association avec l'application Express.
var server = http.createServer(app);
// Association du port 80 au serveur HTTP.
server.listen(80);
Express fournit la méthode listen() pour simplifier encore un peu plus la vie du développeur :
// Instancie un serveur HTTP sur le port 80 et l'associe à
// l'application.
app.listen(80);
Architecture
L’architecture d’Express est basée sur l’exécution en cascade de middlewares (intergiciels en français) à chaque requête entrante.
Un middleware, pour Express, est une fonction qui reçoit en paramètres la requête et la réponse courantes ainsi que le middleware suivant.
Cet exemple de middleware affiche la requête courante puis passe la main au middleware d’après :
function foo(request, response, next) {
// Affiche la requête courante.
console.log('Requête', request.method, 'sur', request.path);
// Passe la main au middleware suivant.
next();
}
L’association d’un middleware à l’application se fait via la méthode use() :
// Associe le middleware foo à l'application.
app.use(foo);
Si l’on effectue une requête sur le serveur grâce à un navigateur (http://localhost:80), on peut voir le message d’erreur « Cannot GET / » s’afficher car le middleware foo() n’a pas répondu à la requête. Dans la console du serveur, par contre, on voit la trace de console.log() :
$ node index.js
Requête GET sur /
Requête
L’objet requête, passé en tant que premier paramètre aux middlewares, permet d’accéder, bien évidemment, à la requête faite à l’application.
Les propriétés vues dans les exemples précédents sont method et path, elles contiennent respectivement la méthode HTTP employée (GET, POST, etc.) et le chemin sur lequel celle-ci a été exécutée.
Mais il en existe bien d’autres, par exemple body, qui contient le corps (contenu) de la requête envoyé par exemple par un formulaire ou une API en JSON. Cette propriété n’est pas remplie par défaut par Express, il est nécessaire d’utiliser un middleware pour faire cela. Les middlewares fournis par le paquet body-parser (http://npmjs.com/package/body-parser) sont très certainement les plus utilisés.
Dans l’exemple suivant, on utilise les middlewares pour prendre en charge le JSON et les formulaires :
var bodyParser = require('body-parser');
// Prise en charge du JSON.
app.use(bodyParser.json());
// Prise en charge des formulaires HTML.
app.use(bodyParser.urlencoded());
// Création d'un middleware qui va afficher le corps de la requête.
app.use(function (req, res, next) { ...
Réponse
L’objet réponse permet d’interagir sur la réponse.
Le premier usage est d’envoyer un texte de réponse grâce à la méthode send() :
app.use(function (req, res, next) {
res.send('Hello world!');
});
Dans cet exemple, on peut constater qu’il ne faut pas appeler le middleware suivant (next()) dans le cas où la requête a fini d’être traitée.
Express expose également un message permettant de renvoyer un objet JSON :
app.use(function (req, res, next) {
res.json({
foo: 'bar'
});
});
Il existe même la méthode download() pour proposer un fichier au téléchargement :
app.use(function (req, res, next) {
res.download('/report.pdf');
});
Pour plus d’informations, reportez-vous également à la page http://expressjs.com/4x/api.html#response.
Distribution de fichiers statiques
Pour servir des fichiers statiques, nul besoin d’écrire son propre middleware. En effet, Express embarque le module serve-static (http://npmjs.org/package/serve-static) qui est dédié à cet usage.
Son utilisation est triviale, il suffit d’appeler la fonction express.static() en lui passant en paramètre le chemin du dossier contenant les fichiers à servir :
// Sert tous les fichiers contenus dans le dossier /public.
app.use(express.static(__dirname + '/public'));
La page du paquet contient la documentation du module et décrit les options disponibles.
Routage
Avec ce que vous connaissez, il est déjà possible de répondre aux différentes requêtes dans des middlewares différents. Cependant, il reste nécessaire d’effectuer un certain nombre de tests à la main, par exemple de vérifier si c’est bien le chemin attendu ou si la bonne méthode est utilisée, comme dans l’exemple suivant :
// Sert la page d'accueil.
app.use(function (req, res, next) {
// Si ce n'est pas la page qui nous intéresse, passe la main au
// prochain middleware.
if (req.path !== '/') {
return next();
}
res.send('Accueil');
});
// Sert la page À propos.
app.use(function (req, res, next) {
// Si ce n'est pas la page qui nous intéresse, passe la main au
// prochain middleware.
if (req.path !== '/about') {
return next();
}
res.send('À propos');
});
Mais cet exemple peut également être écrit autrement en s’appuyant sur le routeur d’Express qui peut vérifier lui-même le chemin :
// Sert...