node (4): url, path y stat

Una biblioteca on line nos servirá de excusa para seguir conociendo las posibilidades de node

Aleksandra Ekster

archivado en: JavaScript / 5 mayo, 2015 / taller:

En la entrada anterior de esta serie dedicada a node empezamos a ver el funcionamiento del event loop y los procesos no bloqueantes. Además, vimos que existe un módulo llamado fs que sirve para trabajar con archivos estáticos. En esta entrada y la siguiente aprenderemos más cosas sobre este módulo montando un servidor de páginas estáticas. Por su extensión, he dividido el tema en dos partes; en esta conoceremos los módulos url, path y stat, y en la siguiente empezaremos a ver qué son los chunks y como funciona el streaming.

El módulo url

Este módulo sirve para trabajar con la url de la petición. Su método principal es parse(), que convierte la cadena de una url en un objeto con distintas propiedades a las que podemos acceder mediante la notación de puntos habitual de javaScript. Lo normal es que esta cadena la obtengamos de la petición del cliente, disponible en la propiedad url del objeto request que le pasamos en la función callback a createServer(). En código se entiende mejor:

// Solicitud: miWebMaravillosa.com/index.html

var http = require('http'),

fs = require('fs'),

path = require('path'),

url = require('url');

http.createServer(function(request, response) {

console.log('cadena: ', request.url);

console.log('objeto: ', url.parse(request.url));

}).listen(3000);

Así, por ejemplo, si tuviéramos esta url:

biblioteca/libros/?titulo=Gilgamesh&capitulo=3

Con pathname podríamos obtener la dirección que hay antes de la query string, esto es, de la interrogación en la que se van concatenando parámetros.

var urlParseada = url.parse(request.url);

console.log(urlParseada.pathname);

// biblioteca/libros/

El método parse, además, recibe un segundo parámetro boleano (true o false) para indicar si queremos que la propiedad query, en la que se cachea la query string, sea una cadena (false) o un objeto (true). Por ejemplo, en el caso anterior esto sería una cadena:

urlParseada.query //  "titulo=Gilgamesh&capitulo=3"

pero si indicamos true, nos devolvería un objeto y, por lo tanto, podríamos acceder a los parámetros de la query string directamente.

var urlParseada = url.parse(request.url, true);

console.log(urlParseada.query.titulo); // Gilgamesh

El módulo url tiene más métodos y propiedades interesantes, pero de momento me remito a la documentación para quien quiera profundizar sobre la materia. Vamos ahora con path.

El módulo path

Como indica su nombre, este módulo sirve para trabajar con las rutas. Con su método basename(), por ejemplo, obtenemos el nombre del recurso que se está solicitando:

var ruta = "http://www.biblioteca/catalogo/index.html";

path.basename(ruta); // index.html

Y con extname(), su extensión:

path.extname(ruta); // html

Más cómodo aún si usamos el método parse(), que nos devuelve un objeto con los datos básicos de una url:

{

root: '',

dir: 'http://www.biblioteca/catalogo',

base: 'index.html',

ext: '.html',

name: 'index'

}

Las rutas de una biblioteca

Bueno, pues con lo visto ya podemos ir montando las rutas de una biblioteca. Imaginemos que en el servidor está estructurada de la siguiente manera:

raíz

server.js

/public

- index.html

- horario.html

- cualquier otra página corporativa ...

-/libros

--libro_01.pdf

--libro_02.pdf

Donde server.js es el código de nuestro server node y public el directorio donde guardamos los recursos que se pueden solicitar. En este directorio, además, hay otro subdirectorio con los libros de la biblioteca en formato pdf.

Entonces, lo que necesitamos es que si nos piden una página normal en una url de este tipo:

biblioteca/horario.html

biblioteca/index.html

se cargue la que corresponda; pero si en cambio nos piden un libro del catálogo...

biblioteca/catalogo/?titulo=Gilgamesh

se cargue el pdf que corresponda.

Así, con un exceso de variables y procesos desglosados para que quede más claro lo que vamos haciendo, de momento podríamos ir preparando un ruteador similar a este...

"use strict";

var http = require('http'),

fs = require('fs'),

path = require('path'),

url = require('url');

http.createServer(function(request, response) {

var recursoSolicitado,

recursoParseado,

ruta;

recursoSolicitado = request.url.toLowerCase().trim();

recursoParseado = url.parse(recursoSolicitado, true);

ruta = path.parse(recursoParseado.pathname);

// Si no es el catálogo

if ( ruta.name != "catalogo" ) {

// Cargamos una página estática

} else {

// lanzamos el catálogo de libros

}

}).listen(3000);

El objeto stat

Vamos primero con los archivos estáticos y para eso lo primero que necesitamos es un cortafuegos, es decir, chequear si la página que se está solicitando existe. En caso de que no exista, enviaremos una respuesta con encabezado 404 y el consabido mensaje Not found. Hay varias maneras de hacer esto. Una es con el objeto stats, que devuelve diversos datos (estadísticas, sería la traducción literal) de un recurso.

Así, definimos la ruta apoyándonos en la variable global __dirname, en la que está definida la ruta donde se está ejecutando el archivo del server...

...

if ( ruta.name != "catalogo" ) {

recursoSolicitado = __dirname +"/public/"+ruta.base;

...

Y tratamos de obtener las estadísticas, los datos, del recurso solicitado con el método asíncrono stat(), que también cuenta con otra sintaxis síncrona y, por lo tanto, no recomendable al ser bloqueante. Si el intento da error o  la comprobación isFile() da false, el archivo no existe y mandamos un 404. Algo así:

fs.stat(recursoSolicitado, function(error, stats) {

if ( error || !stats.isFile() ) {

response.writeHead(404, {

"Content-type": "text/html"

});

response.end("<h1>Not found, baby</h1>");

return false; // paramos el proceso

}

});

Sin embargo, en nuestro caso no necesitaríamos recurrir a stat pues, como veremos justo ahora, nos basta con intentar leer un recurso y, si nos da error, lanzar el 404.

Ojo, los métodos fs.exists(path, callback) y fs.existsSync(path) que se utilizaban para comprobar si existe un archivo están obsoletos. No hay que utilizarlos.

Bueno, todo esto se puede armar mejor, pero eso lo veremos en la próxima entrada de esta serie. Por hoy ya está bien 😛

|| Tags: ,

Este artículo aún no ha sido valorado.

¿Te ha parecido útil o interesante esta entrada?
dormido, valoración 1 nadapensativo, valoración 2 un poco sonrisa, valoración 3 a medias guiño, valoración 4 bastante aplauso, valoración 5 mucho

Tú opinión es muy importante, gracias por compartirla!

Los comentarios están cerrados.