One place for hosting & domains

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


      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 :

      Entrez ensuite dans ce dossier :

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

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

      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 :

      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 :

      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 :

      Ouvrez maintenant ce fichier dans un éditeur de texte :

      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 :

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

      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 :

      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 :

      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 :

      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,emailn1,Sammy Shark,[email protected]`);
      };
      ...
      

      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 :

      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,[email protected]

      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 :

      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 :

      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 :

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

      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 :

      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 :

      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 :

      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 :

      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 :

      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.



      Source link

      How to Create an HTTP Proxy Using Squid on Ubuntu 18.04


      Updated by Rajakavitha Kodhandapani

      Written by Linode

      This guide will show you how to create your own HTTP proxy using Squid, a highly customizable proxy/cache application, on Ubuntu 18.04. An HTTP proxy acts as an intermediary between you and the internet. While connected to your Squid HTTP proxy, you will be able to:

      • Anonymously access internet services.
      • Bypass certain regional and local network restrictions.

      Note

      Install Squid

      1. Secure your Linode by completing the instructions in our guide on Securing Your Server, including adding a limited user account and configuring a firewall.

        Note

        This guide is written for a limited, non-root user. Commands that require elevated privileges are prefixed with sudo. If you are not familiar with the sudo command, you can check our Users and Groups guide.
      2. Ensure that your system is up-to-date:

        sudo apt-get update && sudo apt-get upgrade
        
      3. Install Squid using the apt software package manager:

        sudo apt-get install squid
        
      4. Copy the original configuration file to keep as a backup:

        sudo cp /etc/squid/squid.conf /etc/squid/squid.conf.default
        

        Note

        The Squid configuration file includes comprehensive documentation in its commented lines, along with several uncommented rules that will remain active. These default rules should not be modified while you are following this guide. To gain a deeper understanding of Squid’s options and default settings, you can review the full configuration file.

      Configure Client Access

      Now that you have Squid installed on your Linode, you can configure ways for it to accept connections and serve as an HTTP proxy. The following sections provide different ways for your Squid HTTP proxy to authenticate client connections. You can configure Squid to use either or both authentication methods.

      IP Address Authentication

      A simple way to use Squid as an HTTP proxy is to use a client’s IP address for authentication.

      1. Edit the Squid configuration file and add the following lines at the beginning of the file:

        /etc/squid/squid.conf
        1
        2
        
        acl client src 192.0.2.0 # Home IP
        http_access allow client

        Replace client with a name that identifies the client computer that will connect to your Squid HTTP proxy, then replace 192.0.2.0 with the client computer’s IP address. You can also update the optional comment # Home IP to further describe the client.

      2. Alternatively, you can configure multiple clients by adding new acl lines to /etc/squid/squid.conf and including them in the http_access allow line as follows:

        /etc/squid/squid.conf
        1
        2
        3
        
        acl client1 src 192.0.2.0 # Home IP
        acl client2 src 192.0.2.1 # Work IP
        http_access allow client1 client2

        Replace client1 and client2 with names that identify the client computers, then replace 192.0.2.0 and 192.0.2.1 with their corresponding IP addresses. Update the optional comments # Home IP and # Work IP with accurate descriptions to help keep track of multiple clients. Access to the proxy is granted by adding the names defined by each acl to the http_access allow line.

      User/Password Authentication

      You can also configure your Squid HTTP proxy to accept authentication with usernames and passwords.

      1. Install htpasswd by installing the Apache utility programs. If you have installed Apache on your Linode, you will already have it and can skip this step.

        sudo apt-get install apache2-utils
        
      2. Create a file to store Squid users and passwords:

        sudo touch /etc/squid/squid_passwd
        
      3. Change ownership of the password file:

        sudo chown proxy /etc/squid/squid_passwd
        
      4. Create a username password pair, replacing user1 with the name of the user you’d like to add:

        sudo htpasswd /etc/squid/squid_passwd user1
        

        You will be prompted to create a password for this user:

          
        New password:
        Re-type new password:
        Adding password for user user1
        
        

        You can repeat this step at any time to create new users.

      5. Check the location of the nsca_auth file:

        sudo dpkg -L squid | grep ncsa_auth
        
      6. Edit the Squid configuration file and add the following lines at the beginning of the file:

        Note

        Ensure that you update /usr/lib/squid/basic_ncsa_auth below with the location of the nsca_auth file that you checked in the previous step.

        /etc/squid/squid.conf
        1
        2
        3
        
        auth_param basic program /usr/lib/squid/basic_ncsa_auth /etc/squid/squid_passwd
        acl ncsa_users proxy_auth REQUIRED
        http_access allow ncsa_users
      7. To remove a user’s access to the proxy, you must delete the corresponding entry in the squid_passwd file. Each user is represented in the file on a single line in the format of user:passwordhash:

        /etc/squid/squid_passwd
        1
        
        user1:$p948w3nvq3489v6npq396g user2:$q3cn478554387cq34n57vn

        If you are using Nano, the command Control+k will remove the entire line where the cursor rests.

        Once you’ve saved and exited the file, complete user removal by restarting Squid:

        sudo systemctl restart squid
        

      Combined Authentication

      You can combine authentication methods using the same acl definitions that you have added in the previous two sections by using a single http_access rule.

      1. Remove any previous http_access lines you have added.

      2. Edit the Squid configuration file so that the lines you have added at the beginning of the file follow this form:

        /etc/squid/squid.conf
        1
        2
        3
        4
        5
        
        acl client1 src 192.0.2.0 # Home IP
        acl client2 src 192.0.2.1 # Work IP
        auth_param basic program /usr/lib/squid/basic_ncsa_auth /etc/squid/squid_passwd
        acl ncsa_users proxy_auth REQUIRED
        http_access allow client1 client2 ncsa_users

        Note

        Take care to avoid using multiple http_access rules when combining authentication methods, as Squid will follow the rules in the order that they appear. By using a single http_access rule for your acl definitions, you will ensure that several authentication methods will apply to each client that attempts to connect to your Squid HTTP proxy.

      Anonymize Traffic

      Here, you will add rules to mask client IP addresses from the servers that receive traffic from you Squid HTTP proxy. Without these rules, the originating client IP addresses may be passed on through the X-Forwarded For HTTP header.

      Add the following lines at the beginning of the Squid configuration file:

      /etc/squid/squid.conf
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      
      forwarded_for off
      request_header_access Allow allow all
      request_header_access Authorization allow all
      request_header_access WWW-Authenticate allow all
      request_header_access Proxy-Authorization allow all
      request_header_access Proxy-Authenticate allow all
      request_header_access Cache-Control allow all
      request_header_access Content-Encoding allow all
      request_header_access Content-Length allow all
      request_header_access Content-Type allow all
      request_header_access Date allow all
      request_header_access Expires allow all
      request_header_access Host allow all
      request_header_access If-Modified-Since allow all
      request_header_access Last-Modified allow all
      request_header_access Location allow all
      request_header_access Pragma allow all
      request_header_access Accept allow all
      request_header_access Accept-Charset allow all
      request_header_access Accept-Encoding allow all
      request_header_access Accept-Language allow all
      request_header_access Content-Language allow all
      request_header_access Mime-Version allow all
      request_header_access Retry-After allow all
      request_header_access Title allow all
      request_header_access Connection allow all
      request_header_access Proxy-Connection allow all
      request_header_access User-Agent allow all
      request_header_access Cookie allow all
      request_header_access All deny all

      Enable Connections

      Next, you will enable clients to connect to your Squid HTTP proxy.

      1. Save and exit the Squid configuration file.

      2. Restart Squid to enable the rules you have added:

        sudo systemctl restart squid
        
      3. Implement firewall rules to enable port 3128, which is the default service port used by Squid:

        sudo ufw allow 3128/tcp
        

        You can find more information on configuring firewall rules for Ubuntu in our guide on How to Configure a Firewall with UFW.

      Connect to your Squid HTTP Proxy

      Your Squid HTTP proxy is now ready to accept client connections and anonymously handle internet traffic.

      At this point, you can configure your local browser or operating system’s network settings to use your Linode as an HTTP proxy. The settings to do this will vary depending on your OS and browser. Instructions for certain OS and browser settings are located in the More Information section below.

      Generally, connecting to your Squid HTTP proxy requires the following information:

      • The IP address or domain name associated with your Linode.
      • The port that is being used by Squid. The default port is 3128.
      • A username and password if you have configured them for authentication.

      Once you have established your OS or browser settings, test the connection by pointing your browser at a website that tells you your IP address, such as:

      The result should display your Linode’s IP address instead of the IP address of your client computer.

      More Information

      You may wish to consult the following resources for additional information on this topic. While these are provided in the hope that they will be useful, please note that we cannot vouch for the accuracy or timeliness of externally hosted materials.

      This guide is published under a CC BY-ND 4.0 license.



      Source link

      Como criar um servidor Web em Node.js com o módulo HTTP


      O autor selecionou a COVID-19 Relief Fund​​​​​ para receber uma doação como parte do programa Write for DOnations.

      Introdução

      Ao visualizar uma página em seu navegador, você está fazendo uma solicitação a outro computador na Internet, que em resposta, fornece a você a página Web. O computador ao qual você está se comunicando pela Internet é um servidor Web. Um servidor Web recebe solicitações HTTP de um cliente, como seu navegador, e fornece uma resposta HTTP, como uma página HTML ou um JSON de uma API.

      Para que um servidor retorne uma página da Web, vários softwares são envolvidos no processo. Normalmente, estes softwares se enquadram em duas categorias: o front-end e o back-end. O código de front-end lida com a forma como o conteúdo é apresentado, como a cor de uma barra de navegação e o estilo do texto. O código de back-end lida com a forma como os dados são trocados, processados e armazenados. O código que cuida das solicitações de rede do seu navegador, ou que se comunica com o banco de dados é gerenciado, principalmente, pelo código de back-end.

      O Node.js permite que os desenvolvedores utilizem o JavaScript para escrever o código de back-end, embora ele seja tradicionalmente usado no navegador para escrever o código de front-end. Ter o front-end e o back-end juntos reduz o esforço necessário para criar um servidor Web. Esse é o principal motivo pelo qual o Node.js é a escolha mais popular para escrever códigos de back-end.

      Neste tutorial, você aprenderá como desenvolver servidores Web usando o módulo http que está incluído no Node.js. Você desenvolverá servidores Web que podem retornar dados em JSON, arquivos CSV e páginas Web em HTML.

      Pré-requisitos

      • Verifique se o Node.js está instalado em sua máquina de desenvolvimento. Este tutorial utiliza a versão 10.19.0 do Node.js. Para instalar essa versão em macOS ou Ubuntu 18.04, siga os passos descritos no artigo sobre Como instalar o Node.js e criar um ambiente de desenvolvimento local em macOS ou a seção intitulada Instalando usando um PPA, do artigo sobre Como instalar o Node.js no Ubuntu 18.04.
      • A plataforma Node.js pode ser usada para criar servidores Web prontos para uso. Antes de começar, você precisa estar familiarizado com os princípios básicos do Node.js. Para isso, revise nosso guia sobre Como escrever e executar seu primeiro programa em Node.js.
      • Também usamos a programação assíncrona em uma de nossas seções. Se você não estiver familiarizado com a programação assíncrona em Node.js ou com o uso do módulo fs para interagir com arquivos, você pode aprender mais sobre estes assuntos no nosso artigo Como escrever um código assíncrono em Node.js.

      Passo 1 — Criando um servidor HTTP básico

      Começaremos criando um servidor que retorna um texto sem formatação ao usuário. O processo abordará os conceitos fundamentais necessários para configurar um servidor, que fornecerão a base necessária para retornar os formatos de dados mais complexos, como o JSON.

      Primeiro, precisamos configurar um ambiente de programação acessível para fazer nossos exercícios, bem como os outros execícios no artigo. No terminal, crie uma pasta chamada first-servers:

      Então, acesse aquela pasta:

      Agora, crie o arquivo para guardar o código:

      Abra o arquivo em um editor de texto. Utilizaremos o nano, pois ele está disponível no terminal:

      Começaremos carregando o módulo http, que é padrão em todas as instalações do Node.js. Adicione a linha seguinte ao hello.js:

      first-servers/hello.js

      const http = require("http");
      

      O módulo http contém a função de criar o servidor, que veremos depois. Se quiser aprender mais a respeito de módulos em Node.js, consulte nosso artigo Como criar um módulo Node.js.

      Nosso próximo passo será definir duas constantes, o host e a porta em que nosso servidor se associará:

      first-servers/hello.js

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

      Como mencionado anteriormente, os servidores Web aceitam solicitações de navegadores e de outros clientes. Podemos interagir com um servidor Web ao digitar um nome de domínio, que é traduzido para um endereço IP por um servidor DNS. Um endereço IP é uma sequência única de números que identificam uma máquina em uma rede, como a Internet. Para obter mais informações sobre conceitos de nome de domínio, consulte nosso artigo de Introdução à terminologia, componentes e conceitos do DNS.

      O valor localhost é um endereço privado especial que os computadores utilizam para se referir a eles mesmos. Normalmente, ele é equivalente ao endereço IP interno 127.0.0.1 e está disponível apenas para o computador local, ou seja, não está disponível para nenhuma rede local da qual participamos ou para a Internet.

      A porta é um número que os servidores usam como um ponto de extremidade ou uma “passagem” para nosso endereço IP. Em nosso exemplo, utilizaremos a porta 8000 para nosso servidor Web. As portas 8080 e 8000 são normalmente usadas como as portas padrão em processos de desenvolvimento e, na maioria dos casos, os desenvolvedores utilizarão essas portas em vez de outras disponíveis para servidores HTTP.

      Ao vincularmos nosso servidor a este host e porta, conseguiremos acessar nosso servidor ao visitarmos http://localhost:8000 em um navegador local.

      Vamos adicionar uma função especial que em Node.js, chamamos de request listener. Esta função foi criada para processar uma solicitação HTTP de entrada e retornar uma resposta HTTP. A função deve ter dois argumentos: um objeto de solicitação e um objeto de resposta. O objeto de solicitação capta todos os dados da solicitação HTTP que chegam. O objeto de resposta é usado para devolver respostas HTTP para o servidor.

      Queremos que nosso primeiro servidor retorne a seguinte mensagem sempre que alguém o acessar: "My first server!".

      Vamos adicionar a função a seguir:

      first-servers/hello.js

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

      Normalmente, o nome de uma função baseia-se no que ela faz. Por exemplo, se criássemos uma função request listener para retornar uma lista de livros, daríamos a ela o nome listBooks(). Como este é um caso de exemplo, usaremos o nome genérico requestListener.

      Todas as funções request listener em Node,js aceitam dois argumentos: req e res (podemos dar nomes diferentes a eles se quisermos). A solicitação HTTP que o usuário envia é capturada em um objeto Request, que corresponde ao primeiro argumento, req. A resposta HTTP que retornamos para o usuário é formada pela interação com o objeto Response no segundo argumento, res.

      A primeira linha res.writeHead(200); define o código de status HTTP da resposta. Os códigos de status do HTTP indicam o quão bem uma solicitação HTTP foi processada pelo servidor. Neste caso, o código de status 200 corresponde a "OK". Se estiver interessado em aprender sobre os vários códigos HTTP que seus servidores Web podem retornar e o que eles significam, o nosso guia Como resolver problemas comuns de erros de códigos HTTP será um bom ponto de partida.

      A próxima linha da função res.end("My first server!") ; escreve a resposta HTTP e a retorna para o cliente que a solicitou. Esta função retorna todos os dados que o servidor precisa retornar. Neste caso, ele retorna dados de texto.

      Por fim, podemos criar nosso servidor e usar nosso request listener:

      first-servers/hello.js

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

      Salve e saia do nano pressionando CTRL+X.

      Na primeira linha, criamos um novo objeto server através da função createServer() do módulo http. Este servidor aceita solicitações HTTP e as passa para nossa função requestListener().

      Após criarmos nosso servidor, precisaremos associá-lo a um endereço de rede. Faremos isso com o método server.listen(). Ele aceita três argumentos: port, host e uma função de retorno de chamada que é acionada quando o servidor começa a escutar.

      Todos esses argumentos são opcionais, mas é aconselhável especificar qual porta e qual host queremos que o servidor Web utilize. Ao implantar servidores Web para diferentes ambientes, é importante saber a porta e o host nos quais ele está funcionando para configurar o balanceamento de carga ou um alias DNS.

      A função de retorno de chamada registra uma mensagem no nosso console para que possamos saber quando o servidor começou a escutar conexões.

      Nota: embora o requestListener() não utilize o objeto req, ele deve ser o primeiro argumento da função.

      Usando menos de quinze linhas de código, criamos um servidor Web. Vamos vê-lo em ação e testá-lo por completo, executando o programa:

      No console, veremos esta saída:

      Output

      Server is running on http://localhost:8000

      Note que o prompt desaparece. Isso acontece porque o servidor Node.js é um processo de longa duração. Ele só fecha caso encontre um erro que cause falha e encerramento, ou se interrompermos o processo do Node.js que está sendo executado o servidor.

      Em uma janela do terminal separada, nos comunicaremos com o servidor usando o cURL, uma ferramenta CLI que transfere dados para e a partir de uma rede. Digite o seguinte comando para fazer uma solicitação GET HTTP ao nosso servidor em execução:

      • curl http://localhost:8000

      Ao pressionar ENTER, nosso terminal mostrará o seguinte resultado:

      Output

      My first server!

      Acabamos de configurar um servidor e receber nossa primeira resposta do servidor.

      Vamos detalhar o que aconteceu quando testamos nosso servidor. Ao utilizar o cURL, enviamos uma solicitação GET para o servidor em http://localhost:8000. Nosso servidor Node.js escutou as conexões deste endereço. O servidor transmitiu a solicitação para a função requestListener(). A função retornou dados de texto com o código de status 200. Em seguida, o servidor enviou esta resposta de volta ao cURL, que exibiu a mensagem em nosso terminal.

      Antes de continuarmos, vamos sair de nosso servidor em execução pressionando CTRL+C. Isso interrompe a execução de nosso servidor, e nos traz de volta ao prompt de linha de comando.

      Na maioria dos sites que visitamos ou APIs que utilizamos, as respostas do servidor raramente são textos sem formatação. Os formatos de resposta mais comuns que recebemos são páginas HTML e dados JSON. No próximo passo, aprenderemos como retornar respostas HTTP em formatos de dados comuns que encontramos na Web.

      Passo 2 — Retornando tipos diferentes de conteúdo

      A resposta que retornamos a partir de um servidor Web pode ter vários formatos. O JSON e o HTML foram mencionados anteriormente e, além deles, podemos retornar outros formatos de texto, como o XML e o CSV. Por fim, os servidores Web podem retornar dados não textuais, como PDFs, arquivos zip, áudio e vídeo.

      Neste artigo, além do texto sem formatação que acabamos de retornar, você aprenderá como retornar os tipos de dados a seguir:

      Os três tipos de dados são baseados em texto e são formatos populares para o envio de conteúdos na Web. Muitas linguagens e ferramentas de desenvolvimento do servidor possuem recursos para retornar esses tipos diferentes de dados. No contexto do Node.js, precisaremos fazer duas coisas:

      1. Definir o cabeçalho do Content-Type em nossas respostas HTTP com o valor adequado.
      2. Confirmar que o res.end() recebe os dados no formato correto.

      Veremos isso em ação com alguns exemplos. O código que escreveremos nesta seção e em seções posteriores possui muitas semelhanças com o código que escrevemos anteriormente. A maioria das alterações existem dentro da função requestListener(). Criaremos arquivos com este “código modelo” para facilitar o acompanhamento das próximas seções.

      Crie um arquivo novo chamado html.js. Este arquivo será usado mais tarde para retornar texto HTML em uma resposta HTTP. Colocaremos o código modelo aqui e o copiaremos para os outros servidores que retornam vários tipos.

      No terminal, digite o seguinte:

      Abra este arquivo em um editor de texto:

      Copie o “código modelo” Cole o código no 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}`);
      });
      

      Salve e saia do html.js com CTRL+X e, em seguida, retorne ao terminal.

      Vamos copiar este arquivo em dois arquivos novos. O primeiro arquivo terá o objetivo de retornar dados CSV na resposta HTTP:

      O segundo arquivo retornará uma resposta JSON no servidor:

      Os arquivos restantes serão úteis para exercícios posteriores:

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

      Agora estamos prontos para continuar nossos exercícios. Começaremos retornando o JSON.

      Apresentando o JSON

      Mais conhecido como JSON, o JavaScript Object Notation é um formato de troca de dados baseado em texto. Como o nome sugere, ele é derivado de objetos do JavaScript, mas é independente de linguagem e pode ser usado por qualquer linguagem de programação que possa analisar sua sintaxe.

      O JSON é geralmente usado pelas APIs para aceitar e retornar dados. Sua popularidade se deve ao fato de ser um formato de transferência de dados mais leve que os padrões anteriores a ele, como o XML, bem como às ferramentas que permitem que esse dados sejam analisados pelos programas sem muito esforço. Se quiser aprender mais sobre o JSON, leia nosso guia sobre Como trabalhar com JSON em JavaScript.

      Abra o arquivo json.js com o nano:

      Queremos retornar uma resposta JSON. Vamos modificar a função requestListener() para retornar o cabeçalho apropriado a respostas JSON, modificando as linhas destacadas da seguinte maneira:

      first-servers/json.js

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

      O método res.setHeader() adiciona um cabeçalho HTTP à resposta. Os cabeçalhos HTTP são informações adicionais que podem ser anexadas a uma solicitação ou uma resposta. O método res.setHeader() recebe dois argumentos: o nome do cabeçalho e o valor dele.

      O cabeçalho Content-Type é usado para indicar o formato dos dados, também conhecido como tipo de mídia, que está sendo enviado com a solicitação ou resposta. Neste caso, nosso Content-Type é o application/json.

      Vamos retornar o conteúdo JSON ao usuário. Modifique o json.js para que ele se pareça com isto:

      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"}`);
      };
      ...
      

      Assim como antes, diremos aos usuários que as solicitações deles foram bem-sucedidas, retornando um código de status 200. Desta vez, na chamada response.end(), nosso argumento string contém um JSON válido.

      Salve e saia do json.js pressionando CTRL+X. Agora, vamos executar o servidor com o comando node:

      Em outro terminal, vamos acessar o servidor usando o cURL:

      • curl http://localhost:8000

      Ao pressionar ENTER, veremos o seguinte resultado:

      Output

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

      Agora, retornamos com sucesso uma resposta JSON, à semelhança de várias APIs populares que usamos para criar aplicativos. Você precisa sair do servidor em execução, usando CTRL+C, para que possamos retornar ao prompt do terminal padrão. Em seguida, vamos examinar outro formato popular de retorno de dados: o CSV.

      Apresentando o CSV

      O formato de arquivos CSV (valores separados por vírgula) é um padrão de texto muito usado para fornecer dados tabulares. Na maioria dos casos, cada linha é separada por um caractere de nova linha, e cada item da linha é separado por uma vírgula.

      Em nosso espaço de trabalho, abra o arquivo csv.js com um editor de texto:

      Vamos adicionar as linhas seguintes para nossa função requestListener():

      first-servers/csv.js

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

      Desta vez, nosso Content-Type indica que um arquivo CSV está sendo retornado, pois o valor é text/csv. O segundo cabeçalho que adicionamos é o Content-Disposition. Este cabeçalho diz ao navegador como exibir os dados, em particular no navegador, ou como um arquivo separado.

      Ao retornarmos respostas CSV, a maioria dos navegadores modernos baixam automaticamente o arquivo, mesmo se o cabeçalho Content-Disposition não estiver definido. No entanto, ao retornar um arquivo CSV, devemos adicionar este cabeçalho mesmo assim, pois ele nos permite definir o nome do arquivo CSV. Neste caso, sinalizamos ao navegador que este arquivo CSV é um anexo e ele deve ser baixado. Em seguida, dizemos ao navegador que o nome do arquivo é oceanpals.csv.

      Vamos escrever os dados CSV na resposta 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,emailn1,Sammy Shark,[email protected]`);
      };
      ...
      

      Assim como antes, retornarmos um status 200/OK com nossa resposta. Desta vez, nossa chamada para o res.end() tem uma string que é uma CSV válida. A vírgula separa o valor de cada coluna e o caractere de nova linha (n) separa as linhas. Temos duas linhas, uma para o cabeçalho da tabela e outra para os dados.

      Testaremos este servidor no navegador. Salve o csv.js e saia do editor com o CTRL+X.

      Execute o servidor com o comando Node.js:

      Em outro terminal, acesse o servidor pelo cURL:

      • curl http://localhost:8000

      O console mostrará isso:

      Output

      id,name,email 1,Sammy Shark,[email protected]

      Se formos para http://localhost:8000 em nosso navegador, um arquivo CSV será baixado. O nome do arquivo será oceanpals.csv.

      Saia do servidor em execução com CTRL+C, para retornar ao prompt do terminal padrão.

      Ao retornar o JSON e o CSV, abordamos dois casos populares das APIs. Em seguida, vamos abordar como retornar dados para sites que as pessoas visualizam em um navegador.

      Apresentando o HTML

      O HTML, ou HyperText Markup Language, é o formato mais comum utilizado quando queremos que usuários interajam com nosso servidor através de um navegador Web. Ele foi criado para estruturar o conteúdo Web. Os navegadores Web são desenvolvidos para exibir conteúdo HTML, bem como qualquer estilo que adicionarmos com o CSS (outra tecnologia Web de front-end que nos permite alterar a estética de nossos sites).

      Vamos abrir novamente o html.js com nosso editor de texto:

      Modifique a função requestListener() para retornar o cabeçalho de Content-Type apropriado para uma resposta HTML:

      first-servers/html.js

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

      Vamos retornar o conteúdo HTML ao usuário. Adicione as linhas destacadas ao html.js para que se pareça com isso:

      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>`);
      };
      ...
      

      Adicionamos primeiro o código de status HTTP. Em seguida, chamamos o response.end() com um argumento string que contém um HTML válido. Quando acessarmos nosso servidor no navegador, veremos uma página HTML com uma tag de cabeçalho que contém This is HTML.

      Vamos salvar e sair pressionando o CTRL+X. Agora, vamos executar o servidor com o comando node:

      Veremos a mensagem Server is running on http://localhost:8000 quando nosso programa for iniciado.

      Vá para o navegador e visite http://localhost:8000. Nossa página se parecerá com esta:

      Imagem da resposta HTML retornada do servidor Node.js

      Vamos encerrar a execução do servidor com CTRL+C e retornar ao prompt do terminal padrão.

      É comum que o HTML seja escrito em um arquivo separado do código do servidor, como nossos programas do Node.js. Em seguida, veremos como retornar respostas HTML a partir dos arquivos.

      Passo 3 — Apresentando uma página HTML a partir de um arquivo

      Podemos apresentar aos usuários o HTML como strings no Node.js, mas é melhor carregar os arquivos HTML e exibir seu conteúdo a eles. Desta maneira, não precisamos manter strings longas em nosso código Node.js à medida que o arquivo HTML cresce. Isso mantém o código mais conciso e permite que trabalhemos em cada aspecto do nosso site de maneira independente. Esta “separação de problemas” é comum em várias configurações de desenvolvimento Web. Assim, é bom saber como carregar arquivos HTML para permitir esta separação no Node.js.

      Para exibir arquivos HTML aplicaremos o módulo fs para carregar os arquivos e usaremos os dados dele ao escrever nossa resposta HTTP.

      Primeiro, criaremos um arquivo HTML, que será retornado pelo servidor Web quando solicitado. Crie um novo arquivo HTML:

      Abra o index.html em um editor de texto:

      Nossa página Web será bem simples. Ela terá um plano de fundo e exibirá um texto de saudação no centro. Adicione este código ao arquivo:

      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>
      

      Esta página Web mostra duas linhas de texto: Hello Again! e This is served from a file. As linhas aparecem no centro da página, uma acima da outra. A primeira linha de texto é exibida como um título, ou seja, em letras maiores. A segunda linha de texto aparecerá em letras ligeiramente menores. Todo o texto aparecerá na cor branca enquanto a página Web tem plano de fundo laranja.

      Embora não seja o âmbito deste artigo ou série, se estiver interessado em aprender mais sobre HTML, CSS e outras tecnologias Web de front-end, dê uma olhada no guia Introdução à Web do Mozilla.

      Isso é tudo o que precisamos para o HTML. Assim, salve e saia do arquivo com o CTRL+X. Agora, podemos avançar para o código do servidor.

      Para este exercício, trabalharemos com o htmlFile.js. Abra-o com o editor de texto:

      Como temos que ler um arquivo, vamos começar importando o módulo fs:

      first-servers/htmlFile.js

      const http = require("http");
      const fs = require('fs').promises;
      ...
      

      Este módulo contém uma função readFile() que utilizaremos para carregar o arquivo HTML corretamente. Importamos o objeto promessa do tipo variant, de acordo com as práticas modernas recomendadas para o JavaScript. Usamos as promessas por serem sintaticamente mais sucintas em comparação ao retorno de chamada, que teríamos que usar se atribuíssemos o fs para fazer somente o require('fs'). Para aprender mais sobre as práticas recomendadas da programação assíncrona, leia nosso guia Como escrever um código assíncrono no Node.js.

      Queremos que nosso arquivo HTML seja lido quando um usuário solicitar nosso sistema. Começaremos modificando o requestListener() para ler o arquivo:

      first-servers/htmlFile.js

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

      Usamos o método fs.readFile() para carregar o arquivo. Seu argumento possui o __dirname + "/index.html". A variável especial __dirname tem o caminho absoluto de onde o código Node.js está sendo executado. Em seguida, acrescentamos o /index.html para poder carregar o arquivo HTML que criamos mais cedo.

      Agora, vamos retornar a página HTML assim que estiver carregada:

      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);
              })
      };
      ...
      

      Se a promessa fs.readFile() for resolvida com sucesso, ela retornará os dados dela. Usamos o método then() para processar este caso. O parâmetro contents contém os dados do arquivo HTML.

      Primeiramente, definimos o cabeçalho Content-Type para text/html para dizer ao cliente que estamos retornando dados HTML. Em seguida, escrevemos o código do status para indicar que a solicitação foi bem-sucedida. Por fim, enviamos ao cliente a página HTML que carregamos, com os dados na variável contents.

      O método fs.readFile() pode falhar ocasionalmente, por este motivo, precisamos saber como lidar com esta questão quando recebermos um erro. Adicione isso à função 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;
              });
      };
      ...
      

      Salve o arquivo e saia do nano com o CTRL+X.

      Quando uma promessa encontra um erro, ela é rejeitada. Trataremos esta questão usando o método catch(). Ele aceita o erro que o fs.readFile() retorna, define o código de status para 500 sinalizando que um erro interno foi encontrado, e retorna o erro para o usuário.

      Execute nosso servidor com o comando node:

      No navegador Web, visite http://localhost:8000​​​. Você verá esta página:

      Imagem da página HTML carregada a partir de um arquivo no Node.js

      Você retornou uma página HTML a partir de um servidor para o usuário. Você pode encerrar a execução do servidor com o CTRL+C. Você verá o prompt do terminal quando encerrar o servidor.

      Ao escrever um código como este em um ambiente de produção, não é recomendável carregar uma página HTML toda vez que você receber uma solicitação HTTP. Embora essa página HTML tenha cerca de 800 bytes em tamanho, sites mais complexos podem ter megabytes em tamanho. Arquivos muito grandes podem levar um bom tempo para carregar. Se for esperado que seu site tenha um tráfego intenso, é recomendável carregar os arquivos HTML no momento da inicialização, além de salvar o conteúdo deles. Após eles carregarem, defina o servidor e faça-o escutar solicitações em um endereço.

      Para demonstrar este método, veremos como podemos retrabalhar nosso servidor para torná-lo mais eficiente e escalonável.

      Apresentando o HTML adequadamente

      Neste passo, em vez de carregar o HTML para cada solicitação, o carregaremos apenas uma vez, no início. A solicitação retornará os dados que carregamos na inicialização.

      No terminal, abra novamente o script do Node.js com um editor de texto:

      Começaremos adicionando uma nova variável antes de criarmos a função requestListener():

      first-servers/htmlFile.js

      ...
      let indexFile;
      
      const requestListener = function (req, res) {
      ...
      

      Quando executarmos este programa, esta variável reterá o conteúdo do arquivo HTML.

      Agora, vamos reajustar a função requestListener(). Em vez de carregar o arquivo, ele retornará o conteúdo do indexFile:

      first-servers/htmlFile.js

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

      Em seguida, trocaremos a lógica de leitura do arquivo, da função requestListener() para a inicialização do nosso servidor. Faça as seguintes alterações à medida que criamos o servidor:

      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);
          });
      

      Salve o arquivo e saia do nano com o CTRL+X.

      O código que lê o arquivo é parecido com o que escrevemos em nossa primeira tentativa. No entanto, quando a leitura do arquivo for bem-sucedida, salvaremos seu conteúdo em nossa variável global indexFile. Em seguida, iniciamos o servidor com o método listen(). O ponto principal é que o arquivo precisa ser carregado antes do servidor ser executado. Desta maneira, a função requestListener() certamente retornará uma página HTML, pois a variável indexFile não estará vazia.

      Nosso manipulador de erros também foi alterado. Se o arquivo não puder ser carregado, o erro será capturado e exibido em nosso console. Em seguida, saímos do programa Node.js com a função exit() sem iniciar o servidor. Desta maneira, podemos ver a razão pela qual a leitura do arquivo falhou, resolver o problema e, em seguida, iniciar novamente o servidor.

      Nós criamos servidores Web diferentes que retornam vários tipos de dados para um usuário. Até agora, não utilizamos nenhuma solicitação de dados para determinar o que deveria ser retornado. Precisaremos usar dados de solicitação ao configurar rotas ou caminhos diferentes em um servidor Node.js. Por este motivo, veremos a seguir como eles funcionam juntos.

      Passo 4 — Gerenciando rotas usando um objeto de solicitação HTTP

      A maioria dos sites que visitamos ou APIs que utilizamos geralmente possui mais de um ponto de extremidade, para que possamos acessar vários recursos. Um bom exemplo disso seria um sistema de gerenciamento de livros que poderia ser usado em uma biblioteca. Esse sistema precisaria gerenciar não apenas os dados de livros, mas também os dados de autores, para facilitar os processos de catalogação e consulta.

      Embora os dados para livros e autores estejam relacionados, eles são dois objetos diferentes. Nestes casos, os desenvolvedores de software normalmente programam pontos de extremidades diferentes, como uma maneira de indicar aos usuários da API com quais tipos de dados eles estão interagindo.

      Vamos criar um novo servidor para uma biblioteca pequena, com o propósito de retornar dois tipos diferentes de dados. Se um usuário acessar o endereço de nosso servidor em /books, ele receberá uma lista de livros em JSON. Se eles forem para /authors, receberão uma lista de informações do autor em JSON.

      O que fizemos até agora foi retornar a mesma resposta para cada solicitação que recebemos. Vamos ilustrar isso rapidamente.

      Execute novamente nosso exemplo de resposta JSON:

      Em outro terminal, faremos a mesma solicitação cURL de antes:

      • curl http://localhost:8000

      Você verá:

      Output

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

      Agora, vamos testar outro comando curl:

      • curl http://localhost:8000/todos

      Após pressionar Enter, verá o mesmo resultado:

      Output

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

      Nós não desenvolvemos nenhuma lógica especial em nossa função requestListener() que possa processar um pedido cuja URL contenha /todos. Por esse motivo, o Node.js retorna a mesma mensagem JSON por padrão.

      Como queremos desenvolver um servidor de gerenciamento para uma biblioteca pequena, separaremos os tipos de dados retornados de acordo com o ponto de extremidade que o usuário acessar.

      Primeiro, saia do servidor em execução com o CTRL+C.

      Abra o routes.js em seu editor de texto:

      Começaremos armazenando nossos dados JSON em variáveis antes da função 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 }
      ]);
      ...
      

      A variável books é uma string que contém dados JSON para uma matriz de objetos do tipo livro. Cada livro tem um título ou nome, um autor e o ano de publicação.

      A variável authors é uma string que contém o JSON para uma matriz de objetos do tipo autor. Cada autor tem um nome, país de origem e seu ano de nascimento.

      Agora que temos os dados que nossas respostas retornarão, vamos começar a modificar a função requestListener() para retornar as rotas corretas.

      Primeiro, vamos garantir que cada resposta de nosso servidor tenha o cabeçalho Content-Type correto:

      first-servers/routes.js

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

      Agora, queremos retornar o JSON correto de acordo com o caminho da URL que o usuário acessar. Vamos criar uma instrução de switch na URL da solicitação:

      first-servers/routes.js

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

      Para obter o caminho da URL de um objeto de solicitação, precisamos acessar sua propriedade url. Podemos adicionar casos à instrução switch para retornar o JSON apropriado.

      A instrução switch do JavaScript fornece uma maneira de controlar qual código é executado, dependendo do valor de um objeto ou expressão JavaScript (por exemplo, o resultado de operações matemáticas). Se precisar aprender ou recordar como utilizá-las, consulte nosso guia sobre Como usar a instrução switch em JavaScript.

      Vamos seguir em frente adicionando um case para quando o usuário quiser receber nossa lista de livros:

      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
          }
      }
      ...
      

      Definimos nosso código de status para 200, que indica que está tudo bem com a solicitação, e retorna o JSON que contém a lista de nossos livros. Agora, vamos adicionar outro case para nossos autores:

      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
          }
      }
      ...
      

      Assim como antes, o código de status será 200, pois não há nada errado com a solicitação. Desta vez retornamos o JSON que contém a lista de autores.

      Temos que retornar um erro caso o usuário tente acessar qualquer outro caminho. Para fazermos isso, adicionaremos o caso padrão:

      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"}));
          }
      }
      ...
      

      Usamos a palavra-chave default em uma instrução switch para capturar todos os outros cenários que não foram capturados pelos nossos casos anteriores. Definimos o código de status para 404, que indica que a URL que eles estavam procurando não foi encontrada. Em seguida, definimos um objeto JSON que contém uma mensagem de erro.

      Vamos testar nosso servidor para verificar se ele se comporta como o esperado. Em outro terminal, executamos primeiro um comando para ver se recebemos nossa lista de livros:

      • curl http://localhost:8000/books

      Pressione Enter para ver o seguinte resultado:

      Output

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

      Até agora, tudo certo. Vamos fazer o mesmo processo para o /authors. Digite o comando a seguir no terminal:

      • curl http://localhost:8000/authors

      Você verá o seguinte resultado quando o comando for concluído:

      Output

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

      Vamos testar uma URL errada para confirmar que o requestListener() retorna a resposta de erro:

      • curl http://localhost:8000/notreal

      Ao digitar este comando, a seguinte mensagem será exibida:

      Output

      {"error":"Resource not found"}

      Você pode sair da execução do servidor com o CTRL+C.

      Acabamos de criar caminhos diferentes para que os usuários obtenham dados diferentes. Nós também adicionamos uma resposta padrão que retornará um erro HTTP caso o usuário digite uma URL não compatível.

      Conclusão

      Neste tutorial, você criou uma série de servidores HTTP Node.js. Primeiro, você retornou uma resposta de texto básica. Em seguida, avançou para retornar vários tipos de dados a partir de nosso servidor: JSON, CSV e HTML. A partir de então, você conseguiu combinar o carregamento de arquivos com as respostas HTTP para retornar uma página HTML do servidor para o usuário, além de criar uma API que usou informações da solicitação do usuário para determinar quais dados deveriam ser enviados em resposta a esta solicitação.

      Você está preparado para criar servidores Web que podem processar uma variedade de solicitações e respostas. Com esse conhecimento, é possível criar um servidor que retorna várias páginas HTML para usuários em pontos de extremidades diferentes. É possível também criar sua própria API.

      Para aprender mais sobre servidores Web HTTP em Node.js, leia a documentação do Node.js no módulo http. Caso queira continuar aprendendo sobre o Node.js, volte para a página da série Como programar em Node.js.



      Source link