Sirviendo archivos estáticos con Node.js – Código Automatizado

Como todo recurso solicitado en una petición Web, este debe ser suministrado por nuestro servidor, eso no excluye a los recursos como archivos de Hojas de Estilo (.css) y JavaScript (.js), sin olvidar al No menos importante favicon (.ico), existe para nuestro caso dos posibles soluciones:

  • Hacer referencia a nuestros archivos de recursos a través de un link-href / src indicando la url donde se encuentra nuestro recurso, afuera de nuestro servidor de aplicación (un alojamiento de archivos de un tercero).
  • Hacer que nuestro servidor Node.js suministre los archivos Requeridos, definiendo en nuestro Servidor Http de peticiones las responda en forma explicita.

 

Para este articulo, abordaremos el segundo escenario, desde una perspectiva automatizada, y un enfoque mas formalmente. Este es la Segunda parte del articulo “Sirviendo archivos estáticos con Node.js, donde hemos asumido el entendimiento del problema, y lógica de solución.

 

Visualizando el Problema


En toda petición desde un navegador, sin importar que este incluya o no Hojas de estilo (.CSS) o código JavaScript (.js), siempre realizara dos peticiones, uno para cargar la pagina inicial (./index.html), y la otra para solicitar el icono asociado a la pagina favicon.ico (que es el icono asociado de nuestro sitio web), sin importar que este incluya explicita mente la referencia a este (linea 5 del ejemplo de pagina HTML típica).

<!DOCTYPE html>
<html>
   <head>
      <title>Pagina HTML tipica</title>
      <link rel="icon" href="favicon.ico" type="image/x-icon"/>
      <link type="text/css" rel="stylesheet" href="style.css" />
      <script type="text/javascript" src="jquery-1_6.js"></script>
   </head>
   <body>
      <p>Esta pagina incluye icono, css, javascript</p>
   </body>
   <script type="text/javascript">
      $(document).ready(function()
      {
         alert('Una Alerta');
      });
   </script>
</html>

Presentara al menos los siguientes problemas, desde un servidor implementando Node.js:

  • Node.js NO muestra el icono de página favicon
  • Node.js NO cagar correctamente el .CSS (Hoja de estilos)
  • Node.js NO ejecuta los archivos JavaScript relacionados a BootStrap

 

Planteamiento de la Solución


Para una mejor comprensión de nuestro código, definamos primero el escenario.

Sirviendo archivos estáticos con Node.js – Código Automatizado - Entrada Salida procesar peticion http
Sirviendo archivos estáticos con Node.js – Código Automatizado – Entrada Salida procesar peticion http

En base a lo anterior, el Diagrama de flujo, propuesto podría ser algo parecido a lo siguiente:

Sirviendo archivos estaticos con node js - código automatizado - Diagrama de Flujo propuesto
Sirviendo archivos estáticos con node.js – código automatizado – Diagrama de Flujo propuesto

 

A continuación presentamos el código de solución, para los tres escenarios anteriores:

var http = require('http');
var fs = require('fs');       // File System, Modulo encargado del Sistema de Archivos
var path = require('path');   // Path, Modulo encargado de las Rutas de archivos

http.createServer(function (request, response) 
{
   console.log('Iniciando a Recibir Peticiones...');

   var filePath = '.' + request.url;
   if (filePath == './')
      filePath = './index.htm';

   var extname = path.extname(filePath);
   var contentType = 'text/html';
   var encoding = 'utf-8';
   switch (extname) 
   {
      case '.js':
      contentType = 'text/javascript';
      encoding = 'utf-8';
      break;
      case '.css':
      contentType = 'text/css';
      encoding = 'utf-8';
      break;

      case '.ico':
      contentType = 'image/x-icon';
      encoding = 'binary';
      break;
   }

   // Verificando si el Archivo Existe
   path.exists(filePath, function(exists) {

   if (exists) 
   {
      fs.readFile(filePath, function(error, content) 
      {
         // Verificando si el archivo es Valido o Accesible
         if (error) 
         {
            response.writeHead(500);
            response.end();
         }
         else 
         {
            // Procesando la Peticion 
            response.writeHead(200, { 'Content-Type': contentType });
            response.end(content, encoding );
         }
      });
   }
   else 
   {
      response.writeHead(404);
      response.end();
   }
}).listen(8125);
console.log('Server Ejecutandose en http://127.0.0.1:8125/');

En nuestro código de ejemplo no esta en forma clara donde “Validamos la Petición“, que seria el primer filtro, simplemente evaluamos si esta solicitando el directorio Raíz y en base a eso desplegamos el fichero “./index.htm”, en un escenario real, esto seria mas complejo que simplemente mostrar un simple archivo index.htm, posiblemente tengamos una sección de registro de usuarios (./user.html), una sección de contactos (./contact.html) y toda una gama de escenarios de un Aplicativo web real, incluyendo métodos GET/POST/PUT/DELETE, pero para los fines de ilustración, el ejemplo cumple nuestro cometido.

También la ausencia de ese primer filtro, permitirá que nuestro servidor suministre cualquier archivo solicitado, siempre y cuando ese archivo exista, caso contrario solo indicara que el recurso “404: NO fue encontrado”, es recomendable implementar una lista de los archivos permitidos, lo cual agrega una pequeña capa de seguridad y la lógica de este dependerá de la lógica de nuestras necesidades.

Otra cuestión a considerar, es que NO podemos estar accediendo, continua y constantemente a los recursos del disco duro de nuestro servidor por cada petición (utilizar fs.readFile), eso implicaría tiempo de procesamiento y consumo de recursos, lo ideal seria leer 1 sola vez cada archivo y almacenarlo en memoria y solo proporcionarlo cada vez que sea solicitado.

Si has comprendido a plenitud el código, notaras que de alguna manera puede ser utilizado incluso para servir archivos de Tipo Audio y Vídeo, aunque los conceptos básicos del Streaming NO es lo mismo, podrías implementarlos también, realizando algunos cambios drásticos como transmitir el archivo en bloques iguales al buffer de datos, para que una rutina del lado del cliente las reproduzca.

Si consideran que el numero de respuesta (500 y 404), no corresponde a la contestación y posiblemente debieran ser 400 y 404, siéntanse libres de personalizarlo de acuerdo a sus necesidades y lógica de negocio.

Esperando haya sido de utilidad, permitiendo brindar el entendimiento adecuado a resolver sus problemas.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *