Servidor web simple para SPA / PWA en 5 minutos

Cómo crear un servidor web simple usando solo instrucciones estándar de nodejs


A menudo, se requiere un servidor web simple para desarrollar aplicaciones MPA / SPA / PWA. Una vez, en un gran mitin en respuesta a la pregunta: "¿Qué estabas haciendo?", Dije que estaba creando un servidor web para alojar una aplicación PWA. Todos nos reímos durante mucho tiempo y sí, por cierto, PWA no es pegamento. Al igual que SPA, no es un salón de belleza. Estos son todo tipo de aplicaciones web. Y SSR no es un país :-). Si inicia dicha aplicación simplemente abriendo la página de inicio de index.html a través de un navegador, no funcionará como debería, en el mejor de los casos obtendremos una versión sin conexión. Me encanta JavaScript y resolveré el problema utilizando solo los medios disponibles, por así decirlo.


Comencemos con el plan:


  1. Si no hay NodeJS, descargue LTS, instálelo, no cambie la configuración, haga clic en Siguiente
  2. En nuestro lugar apartado, donde se recopilan todos los proyectos, cree la carpeta simple-web-server
  3. En la carpeta del proyecto, ejecute el comando npm init --yes // sin --yes, el inicializador hará muchas preguntas
  4. En el archivo package.json en la sección de scripts , agregue la propiedad y su valor - "main": "index.js" - para que podamos iniciar rápidamente nuestro servidor usando el comando npm run
  5. Crear una carpeta lib Se recomienda poner todo su código en ella, lo que no requiere montaje y pasos adicionales para su funcionamiento.
  6. Cree un archivo index.js en la carpeta lib . Este es nuestro futuro servidor.
  7. Cree una carpeta dist : esta será la carpeta en la que habrá archivos de acceso público, incluido index.html, en otras palabras, la estática que distribuirá nuestro servidor
  8. Abra el archivo /index.js
  9. Escribe un código

Entonces, ¿qué sabemos sobre lo que debe hacer nuestro servidor?


  1. Manejar solicitudes
  2. Leer archivos
  3. Responder a la solicitud con el contenido del archivo

Primero, cree nuestro servidor, impórtelo al archivo idex.js


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

Esta instrucción destruye el objeto del módulo http y asigna una expresión, la función createServer , al identificador de variable createServer .


Cree un nuevo servidor usando la siguiente declaración


  const server = createServer(); 

Cuando va al host del servidor por primera vez, el navegador envía una solicitud para recibir el documento. Por lo tanto, para procesar este evento, necesitamos escuchar dichas solicitudes. El servidor que creamos tiene un método de escucha , como parámetro pasamos el número de puerto 3000.


  const eventsEmitter = server.listen(3000); 

La expresión de este método será un objeto EventEmitter que se almacenará en una variable con el identificador eventsEmitter . Este objeto es observable. Nos suscribimos a sus eventos utilizando la llamada al método on / addEventListener con dos parámetros de función de cadena requeridos. El primer parámetro indica qué eventos son de interés para nosotros; solicite que el segundo sea una función que procesará este evento.


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

Abre el enlace en el navegador


imagen


Entonces, nos decidimos por las instrucciones del depurador. Vemos que como parámetros obtenemos dos objetos req , res . Estos objetos son instancias de objetos de tipo flujo, por lo tanto , req es el flujo de lectura y res es el flujo de escritura.


Procesamos la solicitud y podemos decir que "el asunto está en el sombrero". Todo lo que queda es leer y devolver el archivo. Primero debe comprender qué tipo de archivo necesitamos. En el depurador, después de estudiar todas las propiedades del parámetro req , vi que tiene la propiedad url . Pero solo no hay nada como index.html en él .


Veamos nuevamente nuestro navegador: vemos que no hemos indicado esto explícitamente. Intentemos nuevamente, pero ya especificaremos explícitamente index.html .


imagen


Ahora está claro que el nombre del archivo viene en la solicitud en la propiedad de URL y nada más que esto, no necesitamos leer el archivo de la solicitud. Lo descomponemos usando un par de llaves, especificamos el nombre de la propiedad url y, a través del operador : establecemos un nombre arbitrario usando un identificador de variable válido, en mi caso requestUrl .


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

Genial, ¿qué sigue? En verdad, no me gusta el hecho de que index.html siempre tendrá que especificarse explícitamente, así que resuelva este problema de inmediato. Decidí que la forma más fácil de hacer esto es usar la función estándar extname; está incluida en el paquete estándar
NodeJS del módulo de ruta lo importamos utilizando la siguiente instrucción.


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

Ahora puede llamarlo pasando la expresión del identificador requestUrl como parámetro y obtener la expresión para la cadena del formato aproximado '.extension' . Si la solicitud no especifica explícitamente un archivo, se devolverá una cadena vacía. Usando este principio, agregaremos el valor predeterminado de 'index.html' . Escribimos las siguientes instrucciones


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

Estoy seguro de que el usuario del servidor querrá anular este nombre y también configurar una variable de entorno utilizando la siguiente instrucción


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

En el proceso de variable global , hay mucha información útil, solo enterraré una parte de ella, en particular, la propiedad env que contiene todas las propiedades del entorno del usuario y ya buscaremos DEFAULT_FILE_NAME si el usuario no la especifica; usamos index.html de manera predeterminada.


IMPORTANTE: si el valor de la propiedad de entorno DEFAULT_FILE_NAME no está definido, la asignación de un valor predeterminado no funcionará. Vale la pena recordar esto, pero no ahora, estamos haciendo todo al mínimo :-)

imagen


Ahora que tenemos un enlace relativo al archivo, necesitamos obtener la ruta absoluta al archivo en el sistema de archivos de nuestro servidor. Decidimos que todos los archivos públicos se almacenarán en la carpeta dist , por lo que para obtener la ruta absoluta al archivo , utilizaremos otra función de resolución del módulo que ya conocemos
ruta simplemente indíquelo en la instrucción creada anteriormente en la línea 5


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

A continuación, en la línea 10, escribimos una instrucción que recibirá y guardará la ruta absoluta a la variable filePath. También "wang" de antemano que el nombre de esta carpeta se puede redefinir para mayor flexibilidad. Por lo tanto, amplío la instrucción en la línea 6, agregando el nombre
variable de entorno DIST_FOLDER !


imagen


Ahora todo está listo para leer el archivo. Puede leer un archivo de diferentes maneras Asincrónicamente, Sincrónicamente, o puede usar secuencias . Usaré streams :-) es hermoso y más efectivo, desde el punto de vista de los recursos gastados. Primero, cree un archivo de prueba en la carpeta dist para que haya algo que leer :-)


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

Ahora necesitamos una función que cree una secuencia de lectura de archivos, también se incluye en la distribución estándar de NodeJS, la extraemos del módulo fs usando las siguientes instrucciones


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

y en la línea 12 en el cuerpo del procesador de solicitudes usamos la siguiente declaración


  createReadStream(filePath) 

Como resultado, la instancia del objeto read-stream se devolverá usándolo
podemos cambiar las secuencias de lectura a la secuencia de escritura, también podemos transformar las secuencias y muchas otras cosas útiles. Entonces el parámetro res es un flujo de lectura, ¿verdad?


Intentemos cambiar inmediatamente la secuencia de lectura de archivos que creamos en la secuencia de escritura de resolución para
de esto, en la línea 12 continuamos la instrucción llamando al método de tubería , y como parámetro pasamos nuestra resolución de flujo de escritura


  createReadStream(filePath).pipe(res); 

imagen


¿Eso es todo? Nooo ¿Y quién se encargará de los errores? ¿Qué tipo de errores? Intentemos subir el archivo CSS al archivo index.html, pero no lo crearemos y veremos qué sucede :-)


 <!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> 

imagen


Esperado, pero el servidor se bloqueó! Este no es el caso en absoluto. El hecho es que, por defecto, los errores no se detectan en las secuencias y debe hacerlo usted mismo :-) createReadStream devuelve la secuencia en la que se produce el error. Por lo tanto, agregamos un controlador de errores. Usando la llamada al método on . Especificando el nombre del evento de error y el controlador de una función. Terminará la secuencia de lectura de lectura con un código de respuesta 404.


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

cheque!


imagen


Otra cosa Por cierto, el servidor aún no está listo y si tratamos de abrirlo en otro navegador, la página no funcionará correctamente :-) quien lo adivinó, escriba en los comentarios, ¿qué olvidó hacer? El hecho es que cuando un servidor responde a la solicitud de un servidor con un archivo, una extensión no es suficiente para que el navegador entienda de qué tipo son este archivo y otros navegadores: ni Chrome ni las versiones anteriores funcionarán con los archivos descargados sin especificar el encabezado de respuesta Tipo de contenido. procesar el archivo, entre otras cosas, nuestro servidor debe especificar el tipo MIME. Para ello, crearemos una variable separada con todos los tipos comunes de mayo. También brindamos la oportunidad de expandirlos pasando como una variable de entorno


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

Bueno, ahora necesita especificar de alguna manera el tipo MIME antes de cambiar la secuencia de lectura a la secuencia de escritura. Utilicé los nombres de extensión de archivo como claves, por lo que obtendremos la extensión de archivo con la conocida función extname


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

y con la ayuda del controlador de eventos de tubería , configuramos el tipo MIME deseado


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

Cheque


imagen


Eso es todo: el servidor está listo. Por supuesto, no es perfecto, pero para un comienzo rápido, eso es todo. Si está interesado en desarrollar esta idea, escriba los comentarios :-)


Código completo del proyecto



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


All Articles