Serveur Web simple pour SPA / PWA en 5 minutes

Comment créer un serveur Web simple en utilisant uniquement des instructions nodejs standard


Souvent, un simple serveur Web est nécessaire pour développer des applications MPA / SPA / PWA. Une fois, lors d'un grand rassemblement en réponse à la question: «Que faisiez-vous?», J'ai dit que je levais un serveur Web pour héberger une application PWA. Nous avons tous ri pendant longtemps et oui, au fait, PWA n'est pas de la colle. Comme SPA, ce n'est pas un salon de beauté. Ce sont toutes sortes d'applications Web. Et la SSR n'est pas un pays :-). Si vous lancez une telle application simplement en ouvrant la page de démarrage de index.html via un navigateur, cela ne fonctionnera pas comme il se doit, dans le meilleur des cas, nous obtiendrons une version hors ligne. J'adore JavaScript et vais résoudre le problème en utilisant uniquement les moyens dont je dispose, pour ainsi dire hors de la boîte.


Commençons par le plan:


  1. S'il n'y a pas de NodeJS, téléchargez LTS, installez, ne modifiez pas les paramètres, cliquez sur suivant
  2. Dans notre endroit isolé, où tous les projets sont collectés, créez le dossier simple-web-server
  3. Dans le dossier du projet, exécutez la commande npm init --yes // sans --yes l'initialiseur posera beaucoup de questions
  4. Dans le fichier package.json de la section scripts , ajoutez la propriété et sa valeur - "main": "index.js" - afin que nous puissions démarrer rapidement notre serveur en utilisant la commande npm run
  5. Créer un dossier lib Il est recommandé d'y mettre tout votre code, ce qui ne nécessite pas d'assemblage et d'étapes supplémentaires pour son fonctionnement
  6. Créez un fichier index.js dans le dossier lib . Il s'agit de notre futur serveur.
  7. Créez un dossier dist - ce sera le dossier dans lequel il y aura des fichiers accessibles au public, y compris index.html, en d'autres termes, le statique que notre serveur distribuera
  8. Ouvrez le fichier /index.js
  9. Écrivez du code

Alors, que savons-nous de ce que notre serveur doit faire?


  1. Gérer les demandes
  2. Lire des fichiers
  3. Répondre à la demande avec le contenu du fichier

Créez d'abord notre serveur, importez-le dans le fichier idex.js


const {createServer} = require('http'); 

Cette instruction déstructure l'objet du module http et affecte une expression, la fonction createServer , à l'identificateur de variable createServer .


Créez un nouveau serveur à l'aide de l'instruction suivante


  const server = createServer(); 

Lorsque vous accédez pour la première fois à l'hôte du serveur, le navigateur envoie une demande de réception du document. Par conséquent, afin de traiter cet événement, nous devons écouter ces demandes. Le serveur que nous avons créé a une méthode d' écoute , comme paramètre nous passons le numéro de port 3000.


  const eventsEmitter = server.listen(3000); 

L'expression de cette méthode sera un objet EventEmitter qui sera stocké dans une variable avec l'identifiant eventsEmitter . Cet objet est observable. Nous souscrivons à ses événements à l'aide de l'appel de méthode on / addEventListener avec deux paramètres de fonction de chaîne requis. Le premier paramètre indique quels événements nous intéressent; demander le second est une fonction qui traitera cet événement.


  eventsEmitter.on('request', (req, res) => { debugger; }); 

Ouvrez le lien dans le navigateur


image


Nous avons donc choisi les instructions du débogueur. Nous voyons qu'en tant que paramètres, nous obtenons deux objets req , res . Ces objets sont des instances d'objets de type flux, donc req est le flux de lecture et res est le flux d'écriture.


Nous avons traité la demande et nous pouvons dire que «l'affaire est dans le chapeau». Il ne reste plus qu'à lire et rendre le fichier. Vous devez d'abord comprendre de quel type de fichier nous avons besoin. Dans le débogueur, après avoir étudié toutes les propriétés du paramètre req , j'ai vu qu'il avait la propriété url . Mais seulement, il n'y a rien de tel que index.html .


Regardons à nouveau notre navigateur: nous voyons que nous ne l'avons pas explicitement indiqué. Réessayons, mais nous spécifierons déjà explicitement index.html .


image


Maintenant, il est clair que le nom du fichier vient dans la demande dans la propriété url et rien de plus, nous n'avons pas besoin de lire le fichier de la demande. Nous la décomposons à l'aide d'une paire d'accolades, spécifions le nom de la propriété url et, via l'opérateur :, définissons un nom arbitraire en utilisant un identificateur de variable valide, dans mon cas requestUrl .


  eventsEmitter.addListener('request', ({url: requestUrl}, res) => { debugger }); 

Super, quelle est la prochaine étape? En vérité, je n'aime pas vraiment le fait que index.html devra toujours être explicitement spécifié, alors résolvons ce problème tout de suite. J'ai décidé que la façon la plus simple de le faire est d'utiliser la fonction extname standard ; elle est incluse dans le package standard
NodeJS du module de chemin nous l'importons en utilisant l'instruction suivante.


  const {extname} = require('path'); 

Vous pouvez maintenant l'appeler en passant l'expression d'identifiant requestUrl comme paramètre et obtenir l'expression de la chaîne du format approximatif '.extension' . Si la demande ne spécifie pas explicitement un fichier, une chaîne vide sera retournée. En utilisant ce principe, nous ajouterons la valeur par défaut de 'index.html' . Nous écrivons l'instruction suivante


  const url = extname(requestUrl) === '' ? DEFAULT_FILE_NAME : requestUrl; 

Je suis sûr que l'utilisateur du serveur voudra remplacer ce nom et également configurer une variable d'environnement à l'aide de l'instruction suivante


  const {env: {DEFAULT_FILE_NAME = '/index.html'}} = process;` 

Dans le processus de variable globale , il y a beaucoup d'informations utiles, je vais juste en enterrer une partie, en particulier, la propriété env , il contient toutes les propriétés de l'environnement de l'utilisateur et nous y chercherons déjà DEFAULT_FILE_NAME si l'utilisateur ne le spécifie pas - nous utilisons index.html par défaut.


IMPORTANT: si la valeur de la propriété d'environnement DEFAULT_FILE_NAME est tout sauf indéfinie, l'attribution d'une valeur par défaut ne fonctionnera pas. Cela mérite d'être rappelé, mais pas maintenant, nous faisons tout au minimum :-)

image


Maintenant que nous avons un lien relatif vers le fichier, nous devons obtenir le chemin absolu vers le fichier dans le système de fichiers de notre serveur. Nous avons décidé que tous les fichiers publics seront stockés dans le dossier dist , donc pour obtenir le chemin absolu du fichier , nous utiliserons une autre fonction de résolution du module que nous connaissons déjà
chemin simplement l'indiquer dans l'instruction précédemment créée sur la ligne 5


  const {resolve, extname} = require('path'); 

Ensuite, à la ligne 10, nous écrivons une instruction qui recevra et enregistrera le chemin absolu vers la variable filePath. J'ai également "wang" à l'avance que le nom de ce dossier peut être redéfini pour plus de flexibilité. Par conséquent, je développe l'instruction sur la ligne 6, en ajoutant le nom
variable d'environnement DIST_FOLDER !


image


Maintenant, tout est prêt à lire le fichier. Vous pouvez lire un fichier de différentes manières de manière asynchrone, synchrone ou utiliser des flux . J'utiliserai des streams :-) c'est beau et plus efficace, du point de vue des ressources dépensées. Créez d'abord un fichier de test dans le dossier dist pour qu'il y ait quelque chose à lire :-)


 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> TESTING 1,2,3... </body> </html> 

Maintenant, nous avons besoin d'une fonction qui créera un flux de lecture de fichiers, elle est également incluse dans la distribution standard de NodeJS, nous l'extrayons du module fs en utilisant l'instruction suivante


  const {createReadStream} = require('fs'); 

et sur la ligne 12 dans le corps du processeur de demande, nous utilisons la déclaration suivante


  createReadStream(filePath) 

en conséquence, l'instance de l'objet de flux de lecture sera retournée en l'utilisant
nous pouvons basculer les flux de lecture sur le flux d'écriture, nous pouvons également transformer les flux et beaucoup d'autres choses utiles. Donc, le paramètre res est un flux de lecture, non?


Essayons de basculer immédiatement le flux de lecture de fichiers que nous avons créé dans le flux d'écriture res pour
de cela, sur la ligne 12, nous continuons l'instruction en appelant la méthode pipe , et en tant que paramètre, nous passons notre flux d'écriture res


  createReadStream(filePath).pipe(res); 

image


C'est tout? Nooon. Et qui va gérer les erreurs? Quel genre d'erreurs? Essayons de télécharger le fichier css dans le fichier index.html, mais nous ne le créerons pas et voyons ce qui se passe :-)


 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="index1.css"> </head> <body> TESTING 1,2,3... </body> </html> 

image


Attendu, mais le serveur est tombé en panne! Ce n'est pas du tout le cas. Le fait est que, par défaut, les erreurs ne sont pas détectées dans les flux et que vous devez le faire vous-même :-) createReadStream renvoie le flux dans lequel l'erreur se produit. Par conséquent, nous ajoutons un gestionnaire d'erreurs. Utilisation de l'appel à la méthode on. Spécification du nom de l'événement d' erreur et du gestionnaire d'une fonction. Il mettra fin au flux de lecture en lecture avec un code de réponse 404.


  createReadStream(filePath) .on('error', error => res.writeHead(404).end()) .pipe(res); 

vérifier!


image


Une autre chose. Soit dit en passant, le serveur n'est pas encore prêt et si nous essayons de l'ouvrir dans un autre navigateur, la page ne fonctionnera pas correctement :-) qui l'a deviné, veuillez écrire dans les commentaires, qu'avez-vous oublié de faire? Le fait est que lorsqu'un serveur répond à la demande d'un serveur avec un fichier, une extension ne suffit pas pour que le navigateur comprenne de quel type sont ce fichier et les autres navigateurs: ni Chrome ni les versions plus anciennes ne fonctionneront avec les fichiers téléchargés sans spécifier l'en-tête de réponse Content-Type. entre autres, notre serveur doit spécifier le type MIME. Pour ce faire, nous allons créer une variable distincte avec tous les types de mai courants. Nous offrons également la possibilité de les étendre en passant comme variable d'environnement


  const {env: {DEFAULT_FILE_NAME = '/index.html', DIST_FOLDER = 'dist', DEFAULT_MIME_TYPES = '{}'}} = process; const {text} = mimeTypes = { 'html': 'text/html', 'jpeg': 'image/jpeg', 'jpg': 'image/jpeg', 'png': 'image/png', 'js': 'text/javascript', 'css': 'text/css', 'text': 'plain/text', 'json': 'application/json', ...JSON.parse(DEFAULT_MIME_TYPES) }; 

Eh bien, vous devez maintenant spécifier le type MIME avant de passer du flux de lecture au flux d'écriture. J'ai utilisé les noms d'extension de fichier comme clés, donc nous obtiendrons l'extension de fichier avec la fonction extname familière


  const fileExtension = extname(url).split('.').pop(); 

et avec l'aide du gestionnaire d'événements pipe , nous définissons le type MIME souhaité


 res.on('pipe', () => res.setHeader(contentType, mimeTypes[fileExtension] || text)); 

Vérifier


image


C'est tout - le serveur est prêt. Bien sûr, ce n'est pas parfait, mais pour un démarrage rapide, c'est tout. Si vous souhaitez développer cette idée, merci d'écrire dans les commentaires :-)


Code de projet complet



Source: https://habr.com/ru/post/fr481898/


All Articles