Fil d'Ariane
Mis à jour le samedi 10 juin 2017
  • 10 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

Ce cours existe en eBook.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Une première application avec Node.js

Connectez-vous ou inscrivez-vous pour bénéficier de toutes les fonctionnalités de ce cours !

Les choses sérieuses commencent ! Fini de rigoler, on rentre maintenant dans le vif du sujet.

C'est un des chapitres les plus importants du cours car il introduit de nombreux concepts de Node.js, qui seront pour la plupart nouveaux pour vous. Il faudra donc lire ce chapitre dans un environnement calme, progressivement, et ne pas hésiter à le relire une seconde fois le lendemain pour vous assurer que vous avez bien compris.

Dans ce chapitre, nous allons créer une vraie application Node.js de bout en bout.
Vous allez voir ce que bas niveau veut dire ! Nous allons en effet devoir gérer tous les rouages du serveur web, qui va traiter les requêtes HTTP des visiteurs et leur retourner une page web HTML.

Ce sera pour vous l'occasion d'expérimenter les fameux callbacks dont je vous avais parlé dans le premier chapitre, ces fonctions qui s'exécutent dès lors qu'un évènement survient. Node.js en est rempli, vous ne pourrez pas y échapper ! ;)

Des serveurs web et des threads

Je crois vous l'avoir déjà dit plusieurs fois mais j'ai envie de le répéter une nouvelle fois ici : Node.js est bas niveau. Tellement bas niveau que vous allez devoir faire des choses que vous n'avez pas l'habitude de faire pour que votre programme fonctionne correctement.

Quand vous créez des sites web avec PHP par exemple, vous associez le langage avec un serveur web HTTP comme Apache ou Nginx. Chacun se répartit les rôles :

  • Apache gère les demandes de connexion HTTP au serveur. C'est lui qui fait en quelque sorte la circulation et qui gère les entrées/sorties.

  • PHP exécute le code des fichiers .php et renvoie le résultat à Apache, qui se charge à son tour de l'envoyer au visiteur.

Comme plusieurs visiteurs peuvent demander une page en même temps au serveur, Apache se charge de les répartir et de les exécuter en parallèle dans des threads différents. Chaque thread utilise un processeur différent sur le serveur (ou un noyau de processeur) :

Le serveur Apache est multithread
Le serveur Apache est multithread

Avec Node.js, on n'utilise pas de serveur web HTTP comme Apache. En fait, c'est à nous de créer le serveur ! Génial non ? :D

Node.js est monothread, contrairement à Apache. Cela veut dire qu'il n'y a qu'un seul processus, qu'une seule version du programme qui peut tourner à la fois en mémoire.

Mais... je croyais que Node.js était super rapide parce qu'il pouvait gérer des tonnes de requêtes simultanées... S'il est monothread, il ne peut faire qu'une action à la fois non ?

En effet, il ne peut faire qu'une chose à la fois et ne tourne donc que sur un noyau de processeur. Mais il fait ça de façon ultra efficace, et malgré ça il est quand même beaucoup plus rapide !
Cela est dû à la nature "orientée évènements" de Node.js. Les applications utilisant Node ne restent jamais les bras croisés sans rien faire. Dès qu'il y a une action un peu longue, le programme redonne la main à Node.js qui va effectuer d'autres actions en attendant qu'un évènement survienne pour dire que l'opération est terminée.

Node.js est monothread, mais il est souple grâce aux évènements
Node.js est monothread, mais il est souple grâce aux évènements

Construire son serveur HTTP

Je vous propose d'attaquer le vif du sujet avec ce tout premier code Node.js :

var http = require('http');

var server = http.createServer(function(req, res) {
  res.writeHead(200);
  res.end('Salut tout le monde !');
});
server.listen(8080);

C'est en quelque sorte le "code minimal" pour un projet Node.js. Placez-le dans un fichier que vous appellerez serveur.js (par exemple).

Mais que fait ce code ?! o_O

Il crée un mini-serveur web qui renvoie un message "Salut tout le monde" dans tous les cas, quelle que soit la page demandée. Ce serveur est lancé sur le port 8080 à la dernière ligne.

Disséquons du code

Décomposons le code :

var http = require('http');

require effectue un appel à une bibliothèque de Node.js, ici la bibliothèque "http" qui nous permet de créer un serveur web. Il existe des tonnes de bibliothèques comme celle-là, la plupart pouvant être téléchargées avec NPM, le gestionnaire de paquets de Node.js (on apprendra à l'utiliser plus tard).

La variable http représente un objet JavaScript qui va nous permettre de lancer un serveur web. C'est justement ce qu'on fait avec :

var server = http.createServer();

On appelle la fonction createServer() contenue dans l'objet http et on enregistre ce serveur dans la variable server. Vous remarquerez que la fonction createServer prend un paramètre... et que ce paramètre est une fonction ! C'est pour ça que l'instruction est un peu compliquée, puisqu'elle s'étend sur plusieurs lignes :

var server = http.createServer(function(req, res) {
  res.writeHead(200);
  res.end('Salut tout le monde !');
});

Tout le code ci-dessus correspond à l'appel à createServer(). Il comprend en paramètre la fonction à exécuter quand un visiteur se connecte à notre site.

Notez que vous pouvez faire ça en deux temps comme je vous l'avait dit. La fonction à exécuter est la fonction de callback. On peut la définir avant dans une variable et transmettre cette variable à createServer(). Ainsi, le code ci-dessous est strictement identique au précédent :

// Code identique au précédent

var instructionsNouveauVisiteur = function(req, res) {
  res.writeHead(200);
  res.end('Salut tout le monde !');
}

var server = http.createServer(instructionsNouveauVisiteur);

Il est très important que vous compreniez ce principe car Node.js ne fonctionne que comme ça. Il y a des fonctions de callback de partout, et en général elles sont placées à l'intérieur des arguments d'une autre fonction comme je l'ai fait dans mon premier code. Ca paraît un peu délicat à lire mais vous prendrez vite le pli rassurez-vous. ;)

N'oubliez pas de bien fermer la fonction de callback avec une accolade, puis de fermer les parenthèses d'appel de la fonction qui l'englobe, puis de placer le fameux point-virgule. C'est pour ça que vous voyez les symboles }); à la dernière ligne de mon premier code.

La fonction de callback est donc appelée à chaque fois qu'un visiteur se connecte à notre site. Elle prend 2 paramètres :

  • La requête du visiteur (req dans mes exemples) : cet objet contient toutes les informations sur ce que le visiteur a demandé. On y trouve le nom de la page appelée, les paramètres, les éventuels champs de formulaires remplis...

  • La réponse que vous devez renvoyer (res dans mes exemples) : c'est cet objet qu'il faut remplir pour donner un retour au visiteur. Au final, res contiendra en général le code HTML de la page à renvoyer au visiteur.

Ici, on fait 2 choses très simples dans la réponse :

res.writeHead(200);
res.end('Salut tout le monde !');

On renvoie le code 200 dans l'en-tête de la réponse, qui signifie au navigateur "OK tout va bien" (on aurait par exemple répondu 404 si la page demandée n'existait pas). Il faut savoir qu'en plus du code HTML, le serveur renvoie en général tout un tas de paramètres en en-tête. Il faut connaître la norme HTTP qui indique comment clients et serveurs doivent communiquer pour bien l'utiliser. Voilà encore un exemple de la complexité dûe au fait que Node.js est bas niveau... Mais en même temps ça nous fait comprendre tout un tas de choses. :)

Ensuite, on termine la réponse (avec end()) en envoyant le message de notre choix au navigateur. Ici, on n'envoie même pas de HTML, juste du texte brut.

Enfin, le serveur est lancé et "écoute" sur le port 8080 avec l'instruction :

server.listen(8080);

Tester le serveur HTTP

Pour tester votre premier serveur, rendez-vous dans la console et tapez :

node serveur.js

La console n'affiche rien et ne répond pas, ce qui est parfaitement normal. Ouvrez maintenant votre navigateur et rendez-vous à l'adresse http://localhost:8080. Vous allez vous connecter sur votre propre machine sur le port 8080 sur lequel votre programme Node.js est en train d'écouter !

Notre premier programme Node.js s'affiche dans le navigateur !
Notre premier programme Node.js s'affiche dans le navigateur !

Et voilà le travail ! :soleil:

Pour arrêter votre serveur Node.js, retournez dans la console et faites Ctrl + C pour couper la commande.

Retourner du code HTML

Bon résumons ! Nous avons créé notre première vraie application avec son serveur web embarqué. Mais l'application est pour l'instant minimaliste :

  • Le message renvoyé est du texte brut, il ne comporte même pas de HTML !

  • L'application renvoie toujours le même message, quelle que soit la page appelée (http://localhost:8080, http://localhost:8080/mapage, http://localhost:8080/dossier/autrepage)

Pour que ce chapitre soit complet, nous allons voir comment remédier à ces deux problèmes. Commençons ici par voir comment faire pour renvoyer du HTML.

Comme je vous l'ai dit, il y a des règles à respecter entre le client et le serveur. Ils communiquent en se basant sur la norme HTTP inventée par Tim Berners-Lee. Cette norme est à la base du Web (tout comme le langage HTML qui a aussi été inventé par ce même monsieur ;) ).

Que dit la norme HTTP ? Que le serveur doit indiquer le type de données qu'il s'apprête à envoyer au client. Eh oui, un serveur peut renvoyer différents types de données :

  • Du texte brut : text/plain

  • Du HTML : text/html

  • Du CSS : text/css

  • Une image JPEG : image/jpeg

  • Une vidéo MPEG4 : video/mp4

  • Un fichier ZIP : application/zip

  • etc.

Ce sont ce qu'on appelle les types MIME. Ils sont envoyés dans l'en-tête de la réponse du serveur.
Vous vous souvenez comment on écrit dans l'en-tête de la réponse avec Node.js ? Nous avions écrit ceci :

res.writeHead(200);

Nous avions seulement indiqué le code de réponse 200 qui signifie "OK, pas d'erreur". Nous devons rajouter un paramètre qui indique le type MIME de la réponse. Pour HTML, ce sera donc :

res.writeHead(200, {"Content-Type": "text/html"});

Maintenant que c'est fait, nous pouvons renvoyer du HTML dans la réponse !

res.end('<p>Voici un paragraphe <strong>HTML</strong> !</p>');

Au final, notre code ressemble donc maintenant à ceci :

var http = require('http');

var server = http.createServer(function(req, res) {
    res.writeHead(200, {"Content-Type": "text/html"});
    res.end('<p>Voici un paragraphe <strong>HTML</strong> !</p>');
});
server.listen(8080);

Essayez-le comme vous l'avez appris, en lançant l'application avec la commandenodedans la console et en ouvrant votre navigateur sur http://localhost:8080 :

Un paragraphe HTML renvoyé par notre appli Node.js
Un paragraphe HTML renvoyé par notre appli Node.js

Votre paragraphe de texte s'affiche et est bien mis en forme comme prévu ! :)

Mais... le code HTML n'est pas valide non ?! On n'a pas écrit de doctype, ni la balise <html>, ni la balise <body>... :euh:

Grmpf, vous m'avez pris la main dans le sac ! Un code HTML invalide, j'ai honte. :honte:

Allez réparons ça ! C'est facile, il suffit d'envoyer toutes les autres balises qui manquent.

var http = require('http');

var server = http.createServer(function(req, res) {
    res.writeHead(200, {"Content-Type": "text/html"});
    res.write('<!DOCTYPE html>'+
'<html>'+
'    <head>'+
'        <meta charset="utf-8" />'+
'        <title>Ma page Node.js !</title>'+
'    </head>'+ 
'    <body>'+
'     	<p>Voici un paragraphe <strong>HTML</strong> !</p>'+
'    </body>'+
'</html>');
    res.end();
});
server.listen(8080);

Waaah ! Mais c'est atroce d'écrire du HTML comme ça ! :waw:

Hé ho, on fait ce qu'on peut avec ce qu'on a !
Node.js est bas niveau, je vous l'ai pas déjà dit une bonne centaine de fois ? :p

Je vous rassure, aucun développeur ne s'amusera vraiment à faire des pages web HTML complexes comme ça là-dedans. Il existe des moyens de séparer le code HTML du code JavaScript : ce sont les systèmes de templates. C'est un peu hors sujet pour le moment, étant donné qu'on commence tout juste à découvrir les bases de Node.js. Mais si le sujet vous intéresse, sachez qu'il existe des tonnes de modules Node.js dédiés aux templates. Le choix est immense !

Déterminer la page appelée et les paramètres

Nous savons renvoyer du code HTML, mais pour le moment notre application... renvoie toujours la même chose ! Comment fait-on pour créer différentes pages avec Node.js ?

Essayez notre petite application sur différentes URLs. Quelle que soit la page appelée...

  • http://localhost:8080

  • http://localhost:8080/mapage

  • http://localhost:8080/dossier/autrepage...)

... la page qui s'affiche est toujours la même !

Il faut qu'on sache quelle est la page demandée par le visiteur. Pour l'instant, vu qu'on ne fait aucun test, notre application renvoie toujours la même chose.

Nous allons découvrir comment récupérer :

  • Le nom de la page demandée (/mapage, /page.html, /dossier/autrepage...)

  • Les paramètres qui circulent dans l'URL (ex : http://localhost:8080/mapage?nom=dupont&prenom=robert).

Quelle est la page demandée par le visiteur ?

Pour récupérer la page demandée par le visiteur, on va faire appel à un nouveau module de Node appelé "url". On demande son inclusion avec :

var url = require("url");

Ensuite, il nous suffit de "parser" la requête du visiteur comme ceci pour obtenir le nom de la page demandée :

url.parse(req.url).pathname;

Voici un code très simple qui nous permet de tester ça :

var http = require('http');
var url = require('url');

var server = http.createServer(function(req, res) {
    var page = url.parse(req.url).pathname;
    console.log(page);
    res.writeHead(200, {"Content-Type": "text/plain"});
    res.write('Bien le bonjour');
    res.end();
});
server.listen(8080);

Exécutez ce script et lancez votre navigateur à l'adresse http://localhost:8080 pour commencer. Retournez ensuite dans la console. Nous y loggons le nom de la page demandée. Vous devriez y voir :

/
/favicon.ico

Je n'ai chargé que la page d'accueil, pourquoi est-ce que je vois /favicon.ico ?

La plupart des navigateurs font en réalité une seconde requête pour récupérer l'icône du site (la "favicon" qu'on voit dans les onglets en général). C'est normal, ne vous en préoccupez pas.

Essayez maintenant de charger des "fausses pages" de votre site pour voir ce que ça fait.

/testpage
/favicon.ico
/un/long/chemin/
/favicon.ico
/faussepage.html
/favicon.ico

Si on omet les favicon.ico qui viennent un peu polluer la console, on voit que j'ai essayé de charger les pages suivantes :

Bon et alors ? Mon site renvoie toujours la même chose quelle que soit la page appelée malgré tout !

Eh bien vous êtes des grands, vous devriez savoir comment faire pour renvoyer un message différent en fonction de la page demandée ! Non ? Une petite condition, un petit if, ça vous dit rien ? ;)

var http = require('http');
var url = require('url');

var server = http.createServer(function(req, res) {
    var page = url.parse(req.url).pathname;
    console.log(page);
    res.writeHead(200, {"Content-Type": "text/plain"});
    if (page == '/') {
        res.write('Vous êtes à l\'accueil, que puis-je pour vous ?');
    }
    else if (page == '/sous-sol') {
        res.write('Vous êtes dans la cave à vins, ces bouteilles sont à moi !');
    }
    else if (page == '/etage/1/chambre') {
        res.write('Hé ho, c\'est privé ici !');
    }
    res.end();
});
server.listen(8080);

Hé oui, c'est basique, c'est roots, c'est parce que Node.js est bas niv... bon OK je me tais. :D

Allez un petit défi pour vous entraîner : faites en sorte d'afficher un message d'erreur si le visiteur demande une page inconnue. Et n'oubliez pas de renvoyer un code d'erreur 404 !

Quels sont les paramètres ?

Les paramètres sont envoyés à la fin de l'URL, après le chemin du fichier. Prenez cette URL par exemple :

http://localhost:8080/page?prenom=Robert&nom=Dupont

Les paramètres sont contenus dans la chaîne ?prenom=Robert&nom=Dupont. Pour récupérer cette chaîne, il suffit de faire appel à :

url.parse(req.url).query

Le problème, c'est qu'on vous renvoie toute la chaîne sans découper au préalable les différents paramètres. Heureusement, il existe un module Node.js qui s'en charge pour nous : querystring !

Incluez ce module :

var querystring = require('querystring');

Vous pourrez ensuite faire :

var params = querystring.parse(url.parse(req.url).query);

Vous disposerez alors d'un tableau de paramètres "params". Pour récupérer le paramètre "prenom" par exemple, il suffira d'écrire : params['prenom'].

Amusons-nous avec un code complet qui affiche votre prénom et votre nom (pourvu que ceux-ci soient définis !) :

var http = require('http');
var url = require('url');
var querystring = require('querystring');

var server = http.createServer(function(req, res) {
    var params = querystring.parse(url.parse(req.url).query);
    res.writeHead(200, {"Content-Type": "text/plain"});
    if ('prenom' in params && 'nom' in params) {
        res.write('Vous vous appelez ' + params['prenom'] + ' ' + params['nom']);
    }
    else {
        res.write('Vous devez bien avoir un prénom et un nom, non ?');
    }
    res.end();
});
server.listen(8080);

Essayez d'aller sur http://localhost:8080?prenom=Robert&nom=Dupont pour voir, puis changez le prénom et le nom pour les remplacer par les vôtres !

Schéma résumé

Allez, résumons ce qu'on vient d'apprendre dans un seul et unique schéma avant de terminer !

Récupérer l'URL et les paramètres avec Node.js
Récupérer l'URL et les paramètres avec Node.js
Exemple de certificat de réussite
Exemple de certificat de réussite