En la publicación pasada, Introducción a Node.js, parte 2: Crear página estática, habíamos visto como crear una aplicación web con archivos estáticos; html, css, js e imágenes; cada vez que un visitante accedía al navegador, a través de la url, la aplicación intentaba buscar un archivo y de encontrarlo y leerlo lo devolvía al navegador, si había algún problema al leerlo devolvía un error 500 y de no encontrarlo un error 404. El código era:
var http = require('http');
var url = require('url');
var fs = require('fs');
var mine_types = {
'js' : 'text/javascript',
'html' : 'text/html',
'css' : 'text/css',
'jpg' : 'image/jpg',
'gif' : 'image/gif',
'png' : 'image/png'
};
http.createServer(function(peticion, respuesta){
var path_nombre = (url.parse(peticion.url).pathname == '/') ? '/index.html' : url.parse(peticion.url).pathname;
var ruta_a_archivo = 'contenido/' + path_nombre;
fs.exists(ruta_a_archivo, function(existe){
if(existe){
fs.readFile(ruta_a_archivo, function(error, contenido_archivo){
if(error){
respuesta.writeHead(500, 'text/plain');
respuesta.end('Error interno.');
}else{
var extension = ruta_a_archivo.split('.').pop();
var mine_type = mine_types[extension];
respuesta.writeHead(200, {'Content-Type': mine_type});
respuesta.end(contenido_archivo);
}
});
}else{
respuesta.writeHead(404, 'text/plain');
respuesta.end('Error 404. El enlace no existe o ha dejado de existir.');
}
});
}).listen(3000, '127.0.0.1');
console.log('El servidor esta funcionando correctamente en https://localhost:3000/');
Este código, se encuentra en el archivo principal de nuestra aplicación, index.js, el cual se compila para hacer funcionar la misma. Sin embargo a medida que nuestro sitio vaya creciendo será difícil mantener el código si lo incluimos todo en nuestro archivo principal.
Módulos
Hasta ahora hemos trabajado con la importación de módulos, por ejemplo el módulo http, que nos permite escuchar las peticiones de los visitantes mediante el método createServer(). En Node.js los módulos funcionan de forma muy parecida a las clases, estos tienen funciones que son invocadas desde el objeto o módulo para realizar distintas operaciones.
Para ello vamos a crear un módulo llamado servidor, el cual nos permitirá hacer todo lo que hicimos hasta ahora de escuchar peticiones y cargar archivos, pero desde un módulo.
Vamos a crear una carpeta a la misma altura de index.js llamado modulos, y dentro de esta carpeta crearemos un archivo llamado servidor.js que tendrá el siguiente código:
var mine_types = {
'js' : 'text/javascript',
'html' : 'text/html',
'css' : 'text/css',
'jpg' : 'image/jpg',
'gif' : 'image/gif',
'png' : 'image/png'
};
function crear(http, url, fs){
http.createServer(function(peticion, respuesta){
var ruta_a_archivo = devolverRutaArchivo(url, peticion);
leerArchivo(fs, ruta_a_archivo, function(numero, contenido_archivo){
if(numero === 404){
respuesta.writeHead(numero, 'text/plain');
respuesta.end('Error 404. El enlace no existe o ha dejado de existir.');
}else if(numero === 500){
respuesta.writeHead(numero, 'text/plain');
respuesta.end('Error interno.');
}else{
var extension = ruta_a_archivo.split('.').pop();
var mine_type = mine_types[extension];
respuesta.writeHead(numero, {'Content-Type': mine_type});
respuesta.end(contenido_archivo);
}
})
}).listen(3000, '127.0.0.1');
}
function devolverRutaArchivo(url, peticion){
var path_nombre = (url.parse(peticion.url).pathname == '/') ? '/index.html' : url.parse(peticion.url).pathname;
var ruta_a_archivo = 'contenido/' + path_nombre;
return ruta_a_archivo;
}
function leerArchivo(fs, ruta_a_archivo, callback){
fs.exists(ruta_a_archivo, function(existe){
if(existe){
fs.readFile(ruta_a_archivo, function(error, contenido_archivo){
if(error){
callback(500, null);
}else{
callback(200, contenido_archivo);
}
});
}else{
callback(404, null);
}
});
}
exports.crear = crear;
Aquí crearemos una variable con los minetypes, lo que vimos en la última publicación:
var mine_types = {
'js' : 'text/javascript',
'html' : 'text/html',
'css' : 'text/css',
'jpg' : 'image/jpg',
'gif' : 'image/gif',
'png' : 'image/png'
};
Luego tendremos tres funciones. Una que nos devolverá el archivo a leer:
function devolverRutaArchivo(url, peticion){
var path_nombre = (url.parse(peticion.url).pathname == '/') ? '/index.html' : url.parse(peticion.url).pathname;
var ruta_a_archivo = 'contenido/' + path_nombre;
return ruta_a_archivo;
}
Ésta recibirá un objeto url y la petición, y nos devolverá la ubicación del archivo a cargar, por ejemplo «contenido/info.html».
También tendremos una función que buscará ese archivo y de encontrarlo leerlo:
function leerArchivo(fs, ruta_a_archivo, callback){
fs.exists(ruta_a_archivo, function(existe){
if(existe){
fs.readFile(ruta_a_archivo, function(error, contenido_archivo){
if(error){
callback(500, null);
}else{
callback(200, contenido_archivo);
}
});
}else{
callback(404, null);
}
});
}
El mismo recibirá como parámetro un objeto fs, la ruta del archivo a leer y una función de tipo callback que se ejecutará una vez que haya finalizado el proceso de búsqueda y lectura del archivo. Esta función recibirá dos parámetro, el primero será el número, dependiendo de si encontró el archivo será 200, si lo encontró pero no pudo leerlo 500, y si no lo encontró 404. El segundo parámetro será el contenido del archivo, si es que lo encontró y pudo leerlo correctamente, de lo contrario éste será null.
Y tendremos una función principal llamada crear() que recibirá como parámetros objetos http, url y fs; y llamará a las otras dos funciones para devolver la respuesta correspondiente al navegador del cliente:
function crear(http, url, fs){
http.createServer(function(peticion, respuesta){
var ruta_a_archivo = devolverRutaArchivo(url, peticion);
leerArchivo(fs, ruta_a_archivo, function(numero, contenido_archivo){
if(numero === 404){
respuesta.writeHead(numero, 'text/plain');
respuesta.end('Error 404. El enlace no existe o ha dejado de existir.');
}else if(numero === 500){
respuesta.writeHead(numero, 'text/plain');
respuesta.end('Error interno.');
}else{
var extension = ruta_a_archivo.split('.').pop();
var mine_type = mine_types[extension];
respuesta.writeHead(numero, {'Content-Type': mine_type});
respuesta.end(contenido_archivo);
}
})
}).listen(3000, '127.0.0.1');
}
Ésta última es la que tendremos que llamar desde el módulo servidor, para así realizar toda la funcionalidad de escuchar peticiones y devolver archivos estáticos. Para que la función esté disponible, para que pueda llamarse desde el objeto debemos exportarla con:
exports.crear = crear;
Ahora sólo nos queda ir a nuestro archivo index.js e importar el módulo servidor y llamar a crear(). Así que abrimos éste y reemplazamos el código por esto:
var http = require('http');
var url = require('url');
var fs = require('fs');
var servidor = require('./modulos/servidor');
servidor.crear(http, url, fs);
console.log('El servidor esta funcionando correctamente en https://localhost:3000/');
Bueno como se ve en el ejemplo importamos los cuatro módulos http, url, fs y servidor y llamamos a la función crear() pasando como parámetros los objetos correspondientes.
Bien, entonces una de las ventajas de trabajar con módulos creados por nosotros es que podemos dividir el código en distintos archivos y así mantener un orden, ya que si ingresamos todo el código en el archivo principal con el correr del tiempo éste se hará difícil de leer y mantener.
Anterior: Introducción a Node.js, parte 2: Crear página estática
Siguiente: Introducción a Node.js, parte 4: Enviar formularios por GET

Realmente muy interesante el tutorial, voy a estar al pendiente por nuevos articulos, estoy intentando aprender nodejs y realmente a sido de mucha ayuda
José, gracias por pasarte.
Sí, seguiré subiendo material, de hecho yo también empecé con Node hace poquito. Más adelante subiré publicaciones de Express, Jade, conexión a MongoDB y cómo hacer un chat.
Si te sirve, yo estuve aprendiendo bastante con estos videotutoriales del profesor Jesús conde:
http://www.youtube.com/watch?v=vnRGAsZdhx0
Que tengas un buen día.
Saludos!
Muy interesante, útil y bien explicado, mi enhorabuena, eres un crack!!
Exelente tutorial, gracias por el aporte
Gracias a vos por comentar.
Que tengas un lindo día.
Excelente tutorial, preciso!
Gracias
Gracias a vos por comentar.
Saludos!