Tutorial

Comment créer un serveur Web en Node.js avec le module HTTP

Node.jsDevelopmentJavaScriptProgramming Project

L'auteur a choisi le COVID-19 Relief Fund pour recevoir un don dans le cadre du programme Write for DOnations.

Introduction

Lorsque vous visualisez une page web dans votre navigateur, vous faites une demande à un autre ordinateur sur internet, qui vous fournit alors la page web en réponse. L'ordinateur avec lequel vous parlez via l'internet est un serveur web. Un serveur web reçoit des requêtes HTTP d'un client, comme votre navigateur, et fournit une réponse HTTP, comme une page HTML ou JSON d'une API.

De nombreux logiciels sont nécessaires pour qu'un serveur renvoie une page web. Ces logiciels se classent généralement en deux catégories : le frontend et le backend. Le code front-end concerne la manière dont le contenu est présenté, comme la couleur d'une barre de navigation et le style du texte.  Le code back-end concerne la manière dont les données sont échangées, traitées et stockées.  Le code qui traite les requêtes réseau de votre navigateur ou qui communique avec la base de données est principalement géré par le code back-end.

Node.js permet aux développeurs d'utiliser JavaScript pour écrire du code back-end, même si, traditionnellement, il était utilisé dans le navigateur pour écrire du code front-end. Le fait d'avoir le front-end et le back-end ensemble de cette manière réduit l'effort nécessaire pour créer un serveur web, ce qui est une des raisons majeures pour lesquelles Node.js est un choix populaire pour écrire du code back-end.

Dans ce tutoriel, vous apprendrez comment construire des serveurs web en utilisant le module http qui est inclus dans Node.js. Vous allez construire des serveurs web capables de renvoyer des données JSON, des fichiers CSV et des pages web HTML.

Conditions préalables

Étape 1 – Création d'un serveur HTTP de base

Commençons par créer un serveur qui renvoie du texte en clair à l'utilisateur. Cela couvrira les concepts clés nécessaires à la mise en place d'un serveur, qui fournira la base nécessaire pour retourner des formats de données plus complexes comme JSON.

Tout d'abord, nous devons mettre en place un environnement de codage accessible pour effectuer nos exercices, ainsi que les autres dans l'article. Dans le terminal, créez un dossier appelé first-servers :

  • mkdir first-servers

Entrez ensuite dans ce dossier :

  • cd first-servers

Maintenant, créez le fichier qui abritera le code :

  • touch hello.js

Ouvrez le fichier dans un éditeur de texte. Nous utiliserons nano puisqu'il est disponible dans le terminal :

  • nano hello.js

Nous commençons par charger le module http qui est standard avec toutes les installations de Node.js Ajoutez la ligne suivante à hello.js :

first-servers/hello.js
const http = require("http");

Le module http contient la fonction de création du serveur, que nous verrons plus tard. Si vous souhaitez en savoir plus sur les modules dans Node.js, consultez notre article Comment créer un module Node.js.

Notre prochaine étape consistera à définir deux constantes, l'hôte et le port auxquels notre serveur sera lié :

first-servers/hello.js
...
const host = 'localhost';
const port = 8000;

Comme mentionné précédemment, les serveurs web acceptent des requêtes des navigateurs et autres clients. Nous pouvons interagir avec un serveur web en entrant un nom de domaine, qui est traduit en une adresse IP par un serveur DNS. Une adresse IP est une séquence unique de chiffres qui identifie une machine sur un réseau, comme l'internet. Pour plus d'informations sur les concepts de noms de domaine, consultez notre article Introduction à la terminologie, aux composants et aux concepts du DNS.

La valeur localhost est une adresse privée spéciale que les ordinateurs utilisent pour se désigner eux-mêmes.  Elle est généralement l'équivalent de l'adresse IP interne 127.0.0.1 et n'est disponible que pour l'ordinateur local, et non pour les réseaux locaux que nous avons rejoints ou pour l'internet.

Le port est un numéro que les serveurs utilisent comme point d'accès ou “porte” à notre adresse IP. Dans notre exemple, nous utiliserons le port 8000 pour notre serveur web. Les ports 8080 et 8000 sont généralement utilisés comme ports par défaut dans le développement, et dans la plupart des cas, les développeurs les utiliseront plutôt que d'autres ports pour les serveurs HTTP.

Lorsque nous lierons notre serveur à cet hôte et à cet port, nous pourrons atteindre notre serveur en visitant http://localhost:8000 dans un navigateur local.

Ajoutons une fonction spéciale, que nous appelons dans Node.js un request listener. Cette fonction est destinée à traiter une requête HTTP entrante et à renvoyer une réponse HTTP. Cette fonction doit avoir deux arguments, un objet de requête et un objet de réponse. L'objet de requête capture toutes les données de la requête HTTP entrante. L'objet de réponse est utilisé pour renvoyer des réponses HTTP au serveur.

Nous voulons que notre premier serveur renvoie ce message chaque fois que quelqu'un y accède : "My first server!"​.

Ajoutons ensuite cette fonction :

first-servers/hello.js
...

const requestListener = function (req, res) {
    res.writeHead(200);
    res.end("My first server!");
};

La fonction est généralement nommée en fonction de ce qu'elle fait. Par exemple, si nous créons une fonction request listener pour renvoyer une liste de livres, nous la nommerons probablement listBooks(). Comme celui-ci est un exemple, nous utiliserons le nom générique requestListener.

Toutes les fonctions request listener dans Node.js acceptent deux arguments : req et res (nous pouvons les nommer différemment si nous le voulons). La requête HTTP que l'utilisateur envoie est capturée dans un objet Request, qui correspond au premier argument, req. La réponse HTTP que nous renvoyons à l'utilisateur est formée par l'interaction avec l'objet Response en second argument, res.

La première ligne res.writeHead(200); définit le code d'état HTTP de la réponse. Les codes d'état HTTP indiquent comment une requête HTTP a été traitée par le serveur. Dans ce cas, le code d'état 200 correspond à "OK". Si vous souhaitez en savoir plus sur les différents codes HTTP que vos serveurs web peuvent renvoyer avec la signification qu'ils revêtent, notre guide Comment dépanner les codes d'erreur HTTP courants est un bon point de départ.

La ligne suivante de la fonction, res.end("My first server!") ;, renvoie la réponse HTTP au client qui l'a demandée. Cette fonction renvoie toutes les données que le serveur doit renvoyer.  Dans ce cas, elle renvoie des données textuelles.

Enfin, nous pouvons maintenant créer notre serveur et utiliser notre request listener :

first-servers/hello.js
...

const server = http.createServer(requestListener);
server.listen(port, host, () => {
    console.log(`Server is running on http://${host}:${port}`);
});

Enregistrez et quittez nano en appuyant sur CTRL+X.

À la première ligne, nous créons un nouvel objet server via la fonction createServer() du module http. Ce serveur accepte les requêtes HTTP et les renvoie à notre fonction requestListener().

Après avoir créé notre serveur, nous devons le lier à une adresse réseau. Nous le faisons avec la méthode server.listen(). Elle accepte trois arguments : le port, host et une fonction de rappel qui se déclenche lorsque le serveur commence à écouter.

Tous ces arguments sont facultatifs, mais il est bon d'indiquer explicitement quel port et quel hôte nous voulons qu'un serveur web utilise. Lorsque l'on déploie des serveurs web dans différents environnements, il est nécessaire de connaître le port et l'hôte sur lesquels ils fonctionnent pour mettre en place une répartition de charge ou un alias DNS.

La fonction de rappel enregistre un message sur notre console afin que nous puissions savoir quand le serveur a commencé à écouter les connexions.

Remarque : même si requestListener() n'utilise pas l'objet req, il doit toujours être le premier argument de la fonction.

Avec moins de quinze lignes de code, nous avons maintenant un serveur web. Voyons-le en action et testons-le de bout en bout en exécutant le programme :

  • node hello.js

Dans la console, nous verrons cette sortie :

Output
Server is running on http://localhost:8000

Notez que l'invite disparaît. C'est dû au fait qu'un serveur Node.js est un processus de longue durée. Il ne se termine que s'il rencontre une erreur qui le fait planter et quitter, ou si nous arrêtons le processus Node.js qui fait tourner le serveur.

Dans une fenêtre de terminal séparée, nous communiquerons avec le serveur en utilisant cURL, un outil CLI pour transférer des données vers et depuis un réseau. Entrez la commande pour faire une requête HTTP GET à notre serveur en cours d'exécution :

  • curl http://localhost:8000

Lorsque nous appuyons sur ENTER, notre terminal affichera la sortie suivante :

Output
My first server!

Nous avons maintenant mis en place un serveur et nous avons obtenu notre première réponse du serveur.

Décomposons ce qui s'est passé lorsque nous avons testé notre serveur. En utilisant cURL, nous avons envoyé une requête GET au serveur sur http://localhost:8000. Notre serveur Node.js a écouté les connexions à partir de cette adresse.  Le serveur a transmis cette requête à la fonction requestListener(). Cette fonction a renvoyé des données textuelles avec le code d'état 200. Le serveur a alors renvoyé cette réponse à cURL, qui a affiché le message dans notre terminal.

Avant de continuer, quittons notre serveur en cours d'exécution en appuyant sur CTRL+C. Cela interrompt l'exécution de notre serveur, nous renvoyant à l'invite de la ligne de commande.

Dans la plupart des sites web que nous visit ou API nous utilisons, les réponses du serveur sont rarement en texte en clair. Nous obtenons des pages HTML et des données JSON comme formats de réponse courants. Dans la prochaine étape, nous apprendrons comment renvoyer des réponses HTTP dans les formats de données courants que nous rencontrons sur le web.

Étape 2 – Renvoi de différents types de contenu

La réponse que nous recevons d'un serveur web peut prendre plusieurs formats. JSON et HTML ont déjà été mentionnés, et nous pouvons également renvoyer d'autres formats de texte comme XML et CSV. Enfin, les serveurs web peuvent renvoyer des données non textuelles telles que des PDF, des fichiers zippés, des fichiers audio et vidéo.

Dans cet article, en plus du texte clair que nous venons de renvoyer, vous apprendrez comment renvoyer les types de données suivants :

  • JSON
  • CSV
  • HTML

Les trois types de données sont tous basés sur le texte et sont des formats populaires pour la diffusion de contenu sur le web. De nombreux outils et langages de développement côté serveur prennent en charge le retour de ces différents types de données. Dans le contexte de Node.js, nous devons faire deux choses :

  1. Définir l'en-tête Content-Type dans nos réponses HTTP avec la valeur appropriée.
  2. Nous assurert que res.end() reçoit les données au bon format.

Voyons cela en action avec quelques exemples. Le code que nous allons écrire dans cette section et les suivantes présentent de nombreuses similitudes avec le code que nous avons écrit précédemment. La plupart des modifications existent dans la fonction requestListener(). Créons des fichiers avec ce “code modèle” afin de rendre les futures sections plus faciles à suivre.

Créez un nouveau fichier appelé html.js : Ce fichier sera utilisé plus tard pour renvoyer du texte HTML dans une réponse HTTP. Nous allons mettre le code modèle ici et le copier aux autres serveurs qui renvoient différents types.

Dans le terminal, entrez ce qui suit :

  • touch html.js

Ouvrez maintenant ce fichier dans un éditeur de texte :

  • nano html.js

Copions le “code modèle”. Saisissez ceci dans nano :

first-servers/html.js
const http = require("http");

const host = 'localhost';
const port = 8000;

const requestListener = function (req, res) {};

const server = http.createServer(requestListener);
server.listen(port, host, () => {
    console.log(`Server is running on http://${host}:${port}`);
});

Enregistrez et quittez html.js avec CTRL+X, puis retournez au terminal.

Maintenant, copions ce fichier dans deux nouveaux fichiers. Le premier fichier servira à renvoyer les données CSV dans la réponse HTTP :

  • cp html.js csv.js

Le second fichier retournera une réponse JSON dans le serveur :

  • cp html.js json.js

Les autres fichiers seront destinés à des exercices ultérieurs :

  • cp html.js htmlFile.js
  • cp html.js routes.js

Nous sommes maintenant prêts pour continuer nos exercices. Commençons par retourner JSON.

Servir JSON

JavaScript Object Notation, communément appelée JSON, est un format d'échange de données basé sur le texte. Comme son nom l'indique, il est dérivé d'objets JavaScript, mais il est indépendant du langage, ce qui signifie qu'il peut être utilisé par tout langage de programmation capable d'analyser sa syntaxe.

JSON est couramment utilisé par les API pour accepter et renvoyer des données. Sa popularité est due à une taille de transfert de données inférieure aux normes d'échange de données précédentes comme XML, ainsi qu'à l'outillage existant qui permet aux programmes de les analyser sans effort excessif. Si vous souhaitez en savoir plus sur JSON, vous pouvez lire notre guide Comment travailler avec JSON en JavaScript.

Ouvrez le fichier json.js avec nano :

  • nano json.js

Nous voulons renvoyer une réponse JSON. Modifions la fonction requestListener() pour renvoyer l'en-tête approprié de toutes les réponses JSON en changeant les lignes surlignées comme ceci :

first-servers/json.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "application/json");
};
...

La méthode res.setHeader() ajoute un en-tête HTTP à la réponse. Les en-têtes HTTP sont des informations supplémentaires qui peuvent être jointes à une requête ou à une réponse. La méthode res.setHeader() prend deux arguments : le nom de l'en-tête et sa valeur.

L'en-tête Content-Type est utilisé pour indiquer le format des données, également appelé media type, qui sont envoyées avec la requête ou la réponse.  Dans ce cas, notre Content-Type est application/json.

Maintenant, retournons le contenu JSON à l'utilisateur. Modifiez json.js pour qu'il ressemble à ceci :

first-servers/json.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "application/json");
    res.writeHead(200);
    res.end(`{"message": "This is a JSON response"}`);
};
...

Comme précédemment, nous indiquons à l'utilisateur que sa demande a abouti en lui renvoyant un code de statut 200. Cette fois-ci, dans l'appel response.end(), notre argument chaîne de caractères contient un JSON valide.

Enregistrez et quittez json.js en appuyant sur CTRL+X. Maintenant, lançons le serveur avec la commande node :

  • node json.js

Dans un autre terminal, atteignons le serveur en utilisant cURL :

  • curl http://localhost:8000

En appuyant sur ENTER, nous obtiendrons le résultat suivant :

Output
{"message": "This is a JSON response"}

Nous avons maintenant renvoyé avec succès une réponse JSON, tout comme beaucoup d'API populaires avec lesquelles nous créons des applications. Veillez à quitter le serveur en cours d'exécution avec CTRL+C afin que nous puissions retourner à l'invite du terminal standard. Ensuite, examinons un autre format populaire de retour de données : CSV.

Servir CSV

Le format de fichier CSV (Comma Separated Values) est une norme de texte couramment utilisée pour fournir des données tabulaires. Dans la plupart des cas, chaque ligne est séparée par une nouvelle ligne, et chaque élément de la ligne est séparé par une virgule.

Dans notre espace de travail, ouvrons le fichier csv.js avec un éditeur de texte :

  • nano csv.js

Ajoutons les lignes suivantes à notre fonction requestListener() :

first-servers/csv.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "text/csv");
    res.setHeader("Content-Disposition", "attachment;filename=oceanpals.csv");
};
...

Cette fois, notre Content-Type indique qu'un fichier CSV est renvoyé car la valeur est text/csv. Le deuxième en-tête que nous ajoutons est Content-Disposition. Cet en-tête indique au navigateur comment afficher les données, en particulier dans le navigateur ou en tant que fichier séparé.

Lorsque nous renvoyons des réponses CSV, la plupart des navigateurs modernes téléchargent automatiquement le fichier même si l'en-tête Content-Disposition n'est pas défini. Cependant, lorsque nous renvoyons un fichier CSV, nous devons quand même ajouter cet en-tête car il nous permet de définir le nom du fichier CSV. Dans ce cas, nous signalons au navigateur que ce fichier CSV est une pièce jointe et qu'il doit être téléchargé. Nous indiquons ensuite au navigateur que le nom du fichier est oceanpals.csv.

Écrivons les données CSV dans la réponse HTTP :

first-servers/csv.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "text/csv");
    res.setHeader("Content-Disposition", "attachment;filename=oceanpals.csv");
    res.writeHead(200);
    res.end(`id,name,email\n1,Sammy Shark,shark@ocean.com`);
};
...

Comme avant, nous retournons un statut 200/OK avec notre réponse. Cette fois, notre appel à res.end() contient une chaîne de caractères qui est un CSV valide. La virgule sépare la valeur dans chaque colonne et le nouveau caractère de ligne (\n) sépare les lignes. Nous avons deux lignes, l'une pour l'en-tête de la table et l'autre pour les données.

Nous allons tester ce serveur dans le navigateur. Enregistrez csv.js et quittez l'éditeur avec CTRL+X.

Lancez le serveur avec la commande Node.js :

  • node csv.js

Dans un autre Terminal, rejoignons le serveur en utilisant cURL :

  • curl http://localhost:8000

La console affichera ceci :

Output
id,name,email 1,Sammy Shark,shark@ocean.com

Si nous allons sur http://localhost:8000 dans notre navigateur, un fichier CSV sera téléchargé.  Son nom de fichier sera oceanpals.csv.

Quittez le serveur en cours d'exécution avec CTRL+C pour revenir à l'invite standard du terminal.

Après avoir renvoyé JSON et CSV, nous avons couvert deux cas populaires pour les API. Passons maintenant à la manière dont nous renvoyons les données pour les sites web que les gens consultent dans un navigateur.

Servir HTML

HTML, HyperText Markup Language, est le format le plus courant à utiliser lorsque nous voulons que des utilisateurs interagissent avec notre serveur via un navigateur web. Il a été créé pour structurer le contenu web. Les navigateurs web sont conçus pour afficher du contenu HTML, ainsi que tous les styles que nous ajoutons avec le CSS, une autre technologie web front-end qui nous permet de modifier l'esthétique de nos sites web.

Réouvrons html.js avec notre éditeur de texte :

  • nano html.js

Modifiez la fonction requestListener() pour renvoyer l'en-tête Content-Type approprié pour une réponse HTML :

first-servers/html.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "text/html");
};
...

Maintenant, retournons le contenu HTML à l'utilisateur. Ajoutez les lignes surlignées au fichier html.js pour qu'il ressemble à ceci :

first-servers/html.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "text/html");
    res.writeHead(200);
    res.end(`<html><body><h1>This is HTML</h1></body></html>`);
};
...

Nous ajoutons d'abord le code de statut HTTP. Nous appelons ensuite response.end() avec un argument de chaîne contenant du HTML valide. Lorsque nous accédons à notre serveur dans le navigateur, nous verrons une page HTML avec une balise d'en-tête contenant This is HTML.

Enregistrez et quittez en appuyant sur CTRL+X. Maintenant, lançons le serveur avec la commande node :

  • node html.js

Nous verrons Server is running on http://localhost:8000 lorsque notre programme a démarré.

Maintenant, allez dans le navigateur et visitez http://localhost:8000. Notre page ressemblera à ceci :

Image de la réponse HTML renvoyée par le serveur Node.js

Quittons le serveur en cours d'exécution avec CTRL+C et revenons à l'invite standard du terminal.

Il est courant que le HTML soit écrit dans un fichier, séparé du code côté serveur comme nos programmes Node.js. Voyons ensuite comment nous pouvons renvoyer des réponses HTML à partir de fichiers.

Étape 3 – Servir une page HTML à partir d'un fichier

Nous pouvons servir le HTML comme des chaînes dans Node.js à l'utilisateur, mais il est préférable que nous chargeions les fichiers HTML et que nous servions leur contenu. De cette façon, lorsque le fichier HTML se développe, nous n'avons pas à maintenir de longues chaînes dans notre code Node.js, ce qui le rend plus concis et nous permet de travailler sur chaque aspect de notre site web de manière indépendante. Cette “séparation des préoccupations” est courante dans de nombreuses configurations de développement web, il est donc bon de savoir comment charger les fichiers HTML pour les prendre en charge dans Node.js

Pour servir les fichiers HTML, nous chargeons le fichier HTML avec le module fs et utilisons ses données lors de l'écriture de notre réponse HTTP.

Tout d'abord, nous créons un fichier HTML que le serveur web renvoie. Créez un nouveau fichier HTML :

  • touch index.html

Maintenant, ouvrez index.html dans un éditeur de texte :

  • nano index.html

Notre page web sera minimale. Elle aura un fond orange et affichera un texte de salutation au centre. Ajoutez ce code au fichier :

first-servers/index.html
<!DOCTYPE html>

<head>
    <title>My Website</title>
    <style>
        *,
        html {
            margin: 0;
            padding: 0;
            border: 0;
        }

        html {
            width: 100%;
            height: 100%;
        }

        body {
            width: 100%;
            height: 100%;
            position: relative;
            background-color: rgb(236, 152, 42);
        }

        .center {
            width: 100%;
            height: 50%;
            margin: 0;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            color: white;
            font-family: "Trebuchet MS", Helvetica, sans-serif;
            text-align: center;
        }

        h1 {
            font-size: 144px;
        }

        p {
            font-size: 64px;
        }
    </style>
</head>

<body>
    <div class="center">
        <h1>Hello Again!</h1>
        <p>This is served from a file</p>
    </div>
</body>

</html>

Cette page web unique affiche deux lignes de texte :Hello again! et This is served from a file. Les lignes apparaissent au centre de la page, l'une au-dessus de l'autre. La première ligne de texte est affichée dans un titre, ce qui signifie qu'elle serait grande. La deuxième ligne de texte apparaîtra un peu plus petite. Tout le texte apparaîtra en blanc et la page web aura un fond orange.

Bien que ce ne soit pas l'objet de cet article ou de cette série, si vous souhaitez en savoir plus sur le HTML, le CSS et d'autres technologies web frontales, vous pouvez consulter le guide de Mozilla intitulé Premiers pas avec le web.

C'est tout ce dont nous avons besoin pour le HTML, alors enregistrez et quittez le fichier avec CTRL+X. Nous pouvons maintenant passer au code du serveur.

Pour cet exercice, nous travaillerons sur htmlFile.js. Ouvrez-le avec l'éditeur de texte :

  • nano htmlFile.js

Comme nous devons lire un fichier, commençons par importer le module fs :

first-servers/htmlFile.js
const http = require("http");
const fs = require('fs').promises;
...

Ce module contient une fonction readFile() que nous utiliserons pour charger le fichier HTML en place. Nous importons la variante promise en respectant les meilleures pratiques modernes de JavaScript. Nous utilisons les promesses en tant que syntaxe plus succincte que les rappels, que nous devrions utiliser si nous assignions fs à juste require('fs'). Pour en savoir plus sur les meilleures pratiques de programmation asynchrone, vous pouvez lire notre guide Comment écrire du code asynchrone dans Node.js.

Nous voulons que notre fichier HTML soit lu lorsqu'un utilisateur sollicite notre système. Commençons par modifier requestListener() pour lire le fichier :

first-servers/htmlFile.js
...
const requestListener = function (req, res) {
    fs.readFile(__dirname + "/index.html")
};
...

Nous utilisons la méthode fs.readFile() pour charger le fichier. Son argument a __dirname + "/index.html". La variable spéciale __dirname a le chemin absolu de l'emplacement où le code Node.js est en cours d'exécution. Nous ajoutons ensuite /index.html afin de pouvoir charger le fichier HTML que nous avons créé précédemment.

Maintenant, retournons la page HTML une fois qu'elle est chargée :

first-servers/htmlFile.js
...
const requestListener = function (req, res) {
    fs.readFile(__dirname + "/index.html")
        .then(contents => {
            res.setHeader("Content-Type", "text/html");
            res.writeHead(200);
            res.end(contents);
        })
};
...

Si la promesse fs.readFile() se résout avec succès, elle renverra ses données. Nous utilisons la méthode then() pour traiter ce cas. Le paramètre contents contient les données du fichier HTML.

Nous définissons d'abord l'en-tête Content-Type sur text/html pour indiquer au client que nous renvoyons les données HTML. Nous écrivons ensuite le code d'état pour indiquer que la demande a abouti. Nous envoyons enfin au client la page HTML que nous avons chargée avec les données dans la variable contents.

La méthode fs.readFile() peut parfois échouer, c'est pourquoi nous devons gérer ce cas lorsque nous obtenons une erreur. Ajoutez ceci à la fonction requestListener() :

first-servers/htmlFile.js
...
const requestListener = function (req, res) {
    fs.readFile(__dirname + "/index.html")
        .then(contents => {
            res.setHeader("Content-Type", "text/html");
            res.writeHead(200);
            res.end(contents);
        })
        .catch(err => {
            res.writeHead(500);
            res.end(err);
            return;
        });
};
...

Sauvegardez le fichier et quittez nano avec CTRL+X.

Lorsqu'une promesse rencontre une erreur, elle est rejetée. Nous traiterons ce cas avec la méthode catch(). Elle accepte l'erreur que fs.readFile() renvoie, met le code de statut à 500 signifiant qu'une erreur interne a été rencontrée, et renvoie l'erreur à l'utilisateur.

Exécutez notre serveur avec la commande node :

  • node htmlFile.js

Dans le navigateur web, visitez http://localhost:8000. Vous verrez cette page :

Image de la page HTML chargée à partir d'un fichier dans Node.js

Vous avez maintenant renvoyé une page HTML du serveur à l'utilisateur. Vous pouvez quitter le serveur en cours d'exécution avec CTRL+C. Vous verrez le retour de l'invite du terminal lorsque vous le ferez.

Lorsque vous écrivez un code de ce type en production, vous ne voudrez peut-être pas charger une page HTML chaque fois que vous recevez une requête HTTP. Alors que cette page HTML a une taille d'environ 800 octets, les sites web plus complexes peuvent avoir une taille de plusieurs mégaoctets. Le chargement de fichiers volumineux peut prendre un certain temps. Si votre site s'attend à un trafic important, il peut être préférable de charger les fichiers HTML au démarrage et de sauvegarder leur contenu. Une fois qu'ils sont chargés, vous pouvez configurer le serveur et lui faire écouter les requêtes sur une adresse.

Pour démontrer cette méthode, voyons comment nous pouvons retravailler notre serveur pour qu'il soit plus efficace et plus évolutif.

Servir efficacement le HTML

Au lieu de charger le HTML pour chaque requête, dans cette étape, nous le chargerons une fois, au début. La requête renverra les données que nous avons chargées au démarrage.

Dans le terminal, réouvrez le script Node.js avec un éditeur de texte :

  • nano htmlFile.js

Commençons par ajouter une nouvelle variable avant de créer la fonction requestListener() :

first-servers/htmlFile.js
...
let indexFile;

const requestListener = function (req, res) {
...

Lorsque nous exécutons ce programme, cette variable contiendra le contenu du fichier HTML.

Maintenant, réajustons la fonction requestListener(). Au lieu de charger le fichier, il va maintenant renvoyer le contenu de indexFile :

first-servers/htmlFile.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "text/html");
    res.writeHead(200);
    res.end(indexFile);
};
...

Ensuite, nous déplaçons la logique de lecture du fichier de la fonction requestListener() au démarrage de notre serveur. Effectuez les changements suivants au moment de la création du serveur :

first-servers/htmlFile.js
...

const server = http.createServer(requestListener);

fs.readFile(__dirname + "/index.html")
    .then(contents => {
        indexFile = contents;
        server.listen(port, host, () => {
            console.log(`Server is running on http://${host}:${port}`);
        });
    })
    .catch(err => {
        console.error(`Could not read index.html file: ${err}`);
        process.exit(1);
    });

Sauvegardez le fichier et quittez nano avec CTRL+X.

Le code qui lit le fichier est semblable à celui que nous avons écrit lors de notre première tentative. Cependant, lorsque nous réussissons à lire le fichier, nous enregistrons maintenant le contenu dans notre variable globale indexFile. Nous démarrons ensuite le serveur avec la méthode listen() L'essentiel est que le fichier soit chargé avant que le serveur ne soit lancé. De cette façon, la fonction requestListener() sera sûre de renvoyer une page HTML, car indexFile n'est plus une variable vide.

Notre gestionnaire d'erreurs a également changé. Si le fichier ne peut pas être chargé, nous récupérons l'erreur et l'imprimons sur notre console. Nous quittons ensuite le programme Node.js avec la fonction exit() sans démarrer le serveur. De cette façon, nous pouvons voir pourquoi la lecture du fichier a échoué, résoudre le problème, puis redémarrer le serveur.

Nous avons maintenant créé différents serveurs web qui renvoient différents types de données à un utilisateur. Jusqu'à présent, nous n'avons utilisé aucune donnée de requête pour déterminer ce qui doit être renvoyé. Nous devrons utiliser des données de requête lors de la configuration de routes ou de chemins différents dans un serveur Node.js, alors voyons maintenant comment ils fonctionnent ensemble.

Étape 4 – Gestion des routes à l'aide d'un Objet de requête HTTP

La plupart des sites web que nous visitons ou des API que nous utilisons ont généralement plus d'un point d'accès, ce qui nous permet d'accéder à diverses ressources. Un bon exemple serait un système de gestion des livres, qui pourrait être utilisé dans une bibliothèque. Il ne devrait pas seulement gérer les données relatives aux livres, mais aussi les données relatives aux auteurs pour faciliter le catalogage et la recherche.

Même si les données concernant les livres et les auteurs sont liées, il s'agit de deux objets différents. Dans ce cas, les développeurs de logiciels codent généralement chaque objet sur des terminaux différents afin d'indiquer à l'utilisateur de l'API le type de données avec lesquelles ils interagissent.

Créons un nouveau serveur pour une petite bibliothèque, qui renverra deux types de données différents. Si l'utilisateur se rend à l'adresse de notre serveur à /books, il recevra une liste de livres en JSON. S'il se rend à l'adresse /authors, il recevra une liste d'informations sur les auteurs en JSON.

Jusqu'à présent, nous avons répondu de la même manière à toutes les demandes que nous avons reçues. Illustrons cela rapidement.

Relancez notre exemple de réponse JSON :

  • node json.js

Dans un autre terminal, faisons une demande CURL comme précédemment :

  • curl http://localhost:8000

Vous verrez :

Output
{"message": "This is a JSON response"}

Maintenant, essayons une autre commande curl :

  • curl http://localhost:8000/todos

Après avoir appuyé sur Enter, vous verrez le même résultat :

Output
{"message": "This is a JSON response"}

Nous n'avons pas intégré de logique spéciale dans notre fonction requestListener() pour traiter une requête dont l'URL contient /todos, donc Node.js retourne le même message JSON par défaut.

Comme nous voulons construire un serveur de gestion de bibliothèque miniature, nous allons maintenant séparer le type de données qui sont retournées en fonction du point final auquel l'utilisateur accède.

Tout d'abord, quittez le serveur en cours d'exécution avec CTRL+C.

Maintenant, ouvrez routes.js dans votre éditeur de texte :

  • nano routes.js

Commençons par stocker nos données JSON dans des variables avant la fonction requestListener() :

first-servers/routes.js
...
const books = JSON.stringify([
    { title: "The Alchemist", author: "Paulo Coelho", year: 1988 },
    { title: "The Prophet", author: "Kahlil Gibran", year: 1923 }
]);

const authors = JSON.stringify([
    { name: "Paulo Coelho", countryOfBirth: "Brazil", yearOfBirth: 1947 },
    { name: "Kahlil Gibran", countryOfBirth: "Lebanon", yearOfBirth: 1883 }
]);
...

La variable livres est une chaîne qui contient JSON pour un tableau d'objets livres. Chaque livre a un titre ou un nom, un auteur, et l'année de sa publication.

La variable authors est une chaîne de caractères qui contient le JSON pour un tableau d'objets auteur. Chaque auteur a un nom, un pays de naissance et une année de naissance.

Maintenant que nous avons les données que nos réponses vont renvoyer, commençons à modifier la fonction requestListener() pour les renvoyer vers les chemins corrects.

Tout d'abord, nous allons nous assurer que chaque réponse de notre serveur a l'en-tête Content-Type correct :

first-servers/routes.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "application/json");
}
...

Maintenant, nous voulons renvoyer le bon JSON en fonction du chemin URL utilisé par l'utilisateur. Créons une déclaration switch sur l'URL de la requête :

first-servers/routes.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "application/json");
    switch (req.url) {}
}
...

Pour obtenir le chemin de l'URL d'un objet de requête, nous devons accéder à la propriété de son url. Nous pouvons maintenant ajouter des cas à la déclaration switch pour renvoyer le JSON approprié.

L'instruction switch de JavaScript permet de contrôler le code qui est exécuté en fonction de la valeur d'un objet ou d'une expression JavaScript (par exemple, le résultat d'opérations mathématiques). Si vous avez besoin d'une leçon ou d'un rappel sur la manière de les utiliser, consultez notre guide Comment utiliser la déclaration switch en JavaScript.

Continuons en ajoutant un cas pour lequel l'utilisateur veut obtenir notre liste de livres :

first-servers/routes.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "application/json");
    switch (req.url) {
        case "/books":
            res.writeHead(200);
            res.end(books);
            break
    }
}
...

Nous fixons notre code de statut à 200 pour indiquer que la demande est correcte et nous renvoyons le JSON contenant la liste de nos livres. Maintenant, ajoutons un autre cas pour nos auteurs :

first-servers/routes.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "application/json");
    switch (req.url) {
        case "/books":
            res.writeHead(200);
            res.end(books);
            break
        case "/authors":
            res.writeHead(200);
            res.end(authors);
            break
    }
}
...

Comme précédemment, le code de statut sera 200 car la requête est bonne. Cette fois, nous retournons le JSON contenant la liste de nos auteurs.

Nous voulons renvoyer une erreur si l'utilisateur essaie d'aller par un autre chemin. Ajoutons le cas par défaut pour ce faire :

routes.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "application/json");
    switch (req.url) {
        case "/books":
            res.writeHead(200);
            res.end(books);
            break
        case "/authors":
            res.writeHead(200);
            res.end(authors);
            break
        default:
            res.writeHead(404);
            res.end(JSON.stringify({error:"Resource not found"}));
    }
}
...

Nous utilisons le mot-clé default dans une déclaration switch pour capturer tous les autres scénarios non capturés par nos cas précédents. Nous fixons le code de statut à 404 pour indiquer que l'URL qu'il recherchait n'a pas été trouvée. Nous définissons ensuite un objet JSON qui contient un message d'erreur.

Testons notre serveur pour voir s'il se comporte comme nous l'attendons. Dans un autre terminal, lançons d'abord une commande pour voir si nous récupérons notre liste de livres :

  • curl http://localhost:8000/books

Appuyez sur Enter pour voir la sortie suivante :

Output
[{"title":"The Alchemist","author":"Paulo Coelho","year":1988},{"title":"The Prophet","author":"Kahlil Gibran","year":1923}]

Jusqu'à présent, tout va bien. Essayons la même chose pour /authors. Tapez la commande suivante dans le terminal :

  • curl http://localhost:8000/authors

Vous verrez la sortie suivante lorsque la commande sera terminée :

Output
[{"name":"Paulo Coelho","countryOfBirth":"Brazil","yearOfBirth":1947},{"name":"Kahlil Gibran","countryOfBirth":"Lebanon","yearOfBirth":1883}]

Enfin, essayons une URL erronée pour nous assurer que requestListener() renvoie la réponse d'erreur :

  • curl http://localhost:8000/notreal

La saisie de cette commande affichera ce message :

Output
{"error":"Resource not found"}

Vous pouvez quitter le serveur en cours d'exécution avec CTRL+C.

Nous avons maintenant créé différentes possibilités pour que les utilisateurs puissent obtenir des données différentes. Nous avons également ajouté une réponse par défaut qui renvoie une erreur HTTP si l'utilisateur entre une URL que nous ne prenons pas en charge.

Conclusion

Dans ce tutoriel, vous avez réalisé une série de serveurs HTTP Node.js. Vous avez d'abord renvoyé une réponse textuelle de base. Vous avez ensuite demandé de renvoyer différents types de données de notre serveur : JSON, CSV et HTML. À partir de là, vous avez pu combiner le chargement de fichiers avec les réponses HTTP pour renvoyer une page HTML du serveur à l'utilisateur, et créer une API qui utilisait les informations relatives à la requête de l'utilisateur pour déterminer quelles données devaient être envoyées dans sa réponse.

Vous êtes maintenant équipé pour créer des serveurs web qui peuvent traiter une variété de requêtes et de réponses. Grâce à ces connaissances, vous pouvez créer un serveur qui renvoie de nombreuses pages HTML à l'utilisateur à différents endroits. Vous pouvez également créer votre propre API.

Pour en savoir plus sur les serveurs web HTTP dans Node.js, vous pouvez lire la documentation de Node.js sur le module http. Si vous souhaitez poursuivre l'apprentissage de Node.js, vous pouvez retourner à la page de la série Comment coder en Node.js.

0 Comments

Creative Commons License