-
Notifications
You must be signed in to change notification settings - Fork 7
Comenzando con Pillars.js
Bajo un enfoque sencillo y modular te permitirá introducirte al desarrollo web en Node.js con una curva de aprendizaje suave, si eres un programador JS avanzado obtendrás un entorno de trabajo organizado y eficaz.
Para empezar a utilizar Pillars.js necesitas tener instalado Node.js. En el siguiente enlace puedes encontrar como instalar Node.js.
Creamos un nuevo directorio para nuestro primer proyecto Pillars.js y añadimos los dos archivos básicos para comenzar a trabajar: package.json (un descriptor de nuestra aplicación) y app.js (el archivo principal de nuestro proyecto).
El archivo package.json permite configurar multitud de parametros. Incialmente sera suficiente con indicar que utilizaremos Pillars.js en la lista de dependencias:
{
"dependencies": {
"pillars": "*"
}
}
Puedes encontrar más información sobre cómo funciona este archivo en el sitio web de npm.
Ahora utilizaremos npm para cargar nuestras dependencias, utilizando el comando npm install
por consola en el directorio del proyecto. Esto creará un directorio /node_modules en el que se colocarán las dependencias descritas en nuestro archivo package.json.
De forma alternativa puedes instalar directamente el modulo de pillars, sin necesidad del archivo package.json mediante el comando
npm install pillars
en el directorio del proyecto.
Ya podemos empezar a utilizar Pillars.js, en el archivo app.js escribiremos un ejemplo básico para comprobar el funcionamiento:
var project = require('pillars');
project.services.get('http').configure({port:3000}).start();
project.routes.add(new Route({
id: 'Home',
path: '/',
method: ['get','post']
},function(gw){
gw.html("<h1>Hello World!!</h1>");
}));
Podemos arrancar nuestra aplicación ejecutando el comando
node app.js
en el directorio de nuestro proyecto. Para detener la aplicación utilizaremos la combinación de teclasctrl+c
.
Los conceptos principales en los que se basa Pillars.js son:
- Environment/project: Punto central de configuración y gestión de nuestro proyecto, permite añadir controladores de ruta (Route) y Plugins entre otras funciones, se obtiene al hacer el require de 'pillars'.
- Route: Controlador de ruta, clase con la que podremos crear un manejador/controlador y su configuración. Recibe un objeto Gangway como parámetro y son anidables.
- Gangway: Un objeto con todas las propiedades y métodos relacionados con la gestión de una solicitud, es una envoltura 'limpia' para los objetos 'request' y 'response' propios de Node.js.
- Plugin: Clase que permite crear controladores para el entorno general, pueden modificar el funcionamiento de Gangway y Route, y acceder a sus propiedades.
Pillars.js incluye una serie de librerías adicionales desarrolladas de forma paralela al framework:
- Textualization: Ofrece soporte i18n para JS.
- Scheduled: Gestor de tareas programadas al estilo cron.
- Crier: Sistema de gestión de logs.
- Procedure: Utilidad para facilitar la escritura de conjuntos de sentencias asíncronas.
- Templated: Gestor de motores de plantillas con gestión de cache de las compilaciones.
- ObjectArray: Utilizado intensivamente en Pillars.js para organizar/gestionar conjuntos de objetos.
- String.format: Método para la clase String que permite formatear plantillas de texto.
- Date.format: Método para la clase Date que permite formatear fechas de forma sencilla.
- JSON.decycled: Conjunto de métodos para la clase JSON que solucionan los problemas de referencias circulares entre otros.
- JSON.crypt: Utilidad de encriptado/desencriptado de objetos JS.
Al utilizar var project = require('pillars')
obtenemos un referencia al entorno, o lo que es lo mismo, el punto central de trabajo para nuestro proyecto. Nos permite configurar el funcionamiento general del sistema y nos da acceso centralizado a todos sus elementos: estructura de rutas/controladores, Plugins funcionando, Servicios disponibles y otras propiedades del sistema.
Al invocar por primera vez la librería pillars se establecen de forma global las clases más importantes como Route y Plugin que podrás usar inmediatamente.
Pillars.js utiliza los servicios como una entidad compartida por todo el entorno, a los cuales podrán acceder para interactuar con éstos otros módulos que se incluyan en el proyecto. Los servicios ofrecen una estructura transversal para todo el código del proyecto.
Pillars.js tiene disponible un servicio preconfigurado de tipo HttpService, por lo que no es necesario crear uno. (a menos que necesitemos varios servicios HTTP/S).
Accedemos a los servicios por medio de la propiedad project.services
, un ObjectArray en el que podremos localizar cualquiera de los servicios disponibles en el sistema. En el siguiente código puedes ver una configuración sencilla del servidor por defecto.
var project = require('pillars');
project.services.get('http').configure({
port:3000,
timeout: 30*1000,
hostname: 'sub.dom.ext'
}).start();
La configuración de variables de entorno está disponible en project.config
, podremos configurar varias propiedades a la vez mediante el método project.configure({...})
, en el ejemplo vemos algunas de las propiedades disponibles.
var project = require('pillars').configure({
debug: true, // Por defecto false
renderReload: true, // Detiene el uso de cache de templates
cors: false, // determina los orígenes permitidos para CORS
cacheMaxSize : 250*1024*1024, // establece el tamaño máximo de la cache en memoria dedicada a archivos servidos
fileMaxAge : 7*24*60*60, // Caducidad por defecto de los archivos servidos
});
Por último tenemos los métodos que nos permiten administrar las rutas y plugins de nuestra aplicación, todas la rutas/controladores de nuestro proyecto se encuentran en el ObjectArray project.routes
y los Plugins en project.plugins
:
-
project.routes.add(new Route())
: Añade un nuevo controlador al sistema. Por medio deremove()
yget()
podremos eliminar y obtener respectivamente controladores ya insertados o añadidos. Cada objeto route a su vez contiene la propiedad.routes
, en la que podremos añadir sub-controladores, pudiendo crear un árbol de controladores dependientes unos de otras; los controladores anidados heredan las configuraciones de sus antecesores. -
project.plugins.add(new Plugin())
: Añade un nuevo plugin al sistema. Por medio deremove()
yget()
podremos eliminar y modificar respectivamente plugins ya insertados. Pillars incluye por defecto varios plugins que realizan funciones fundamentales para el sistema, entre ellos el más importante es el plugin "router.js".
Clase que permite asociar un manejador(handler) con una configuración concreta. Un objeto Route consiste en un manejador y una serie de propiedades que servirán para definir cuándo y cómo ejecutar éste, la forma más básica de crear un objeto route es declarando sólo su manejador, dejando las opciones por defecto:
var myRoute = new Route(function(gw){
gw.send('¡Hola Mundo!');
});
Aunque en general necesitaremos declarar el objeto definiendo también un objeto de configuración:
var myRoute = new Route({ // Configuración
id:'Status',
path:'admin/status',
method:['get','post']
},function(gw){ // Manejador
gw.html('¡Hola Mundo!');
});
Route establece por defecto valores para las siguientes propiedades si no son especificadas:
- .id: Por defecto se genera un identificador único para la ruta en caso de no especificar uno.
- .path: Por defecto es '/'
- .method: por defecto son todos.
Los objetos route a su vez pueden tener otros objetos route como hijos. Éstos funcionarán como sub-controladores, permitiéndonos organizar el árbol de controladores de forma más orgánica y como veremos más adelante nos servirá para trabajar con propiedades con herencia y tener un control organizado sobre grupos de controladores.
var adminArea = new Route({
id: 'Administration',
path: '/admin'
},function(gw){
gw.html('Admin area');
});
adminArea.routes.add(new Route({
id: 'Status',
path: '/status'
},function(gw){
gw.html('Admin area: Show status');
}))
El objeto Route con el id 'Status' responderá a la ruta '/admin/status'. Si desactivamos al Route con el id 'Administration', dejarán de estar disponibles todos sus descendientes.
También disponemos de la propiedad .active
de Route, que por defecto es true
y nos permite desactivar un Route junto con su correspondiente descendencia.
Es la solicitud HTTP, si ya has trabajado con Node.js estarás acostumbrado a los objetos request
y response
. En Pillars.js estos objetos son propiedades de Gangway, un objeto Gangway consiste en la solicitud del cliente completamente parseada y organizada para su uso. Con cada solicitud al servidor HTTP se crea un objeto Gangway que es recibido por el Route/manejador correspondiente. Algunas de las propiedades y métodos de Gangway:
-
.cookie
: Devuelve un objeto con el contenido de la cookie, si existiese. -
.ua
: Un objeto con información sobre el agente de usuario,.mobile
nos indica (booleano) si el cliente es un dispositivo móvil y el resto de propiedades nos aporta información sobre el sistema operativo y el navegador (os,engine,browser). -
.ip
: La dirección IP del cliente. -
.accepts
: Cabeceras accept ya resueltas y organizadas por prioridad. -
.host
: El nombre de host o dirección IP al que se realiza la petición. -
.method
: El método utilizado por la petición (GET,POST,PUT,DELETE,HEAD). -
.path
: Ruta que se ha solicitado, exclusivamente el valor de la ruta sin la parte host ni query, este valor puede verse modificado internamente si por alguna razón una ruta es redirigida a otro punto del entorno, para conocer el path original tenemos la propiedad.originalPath
. -
.query
: Un objeto con el contenido del query de la petición. -
.params
: Un objeto con todos los parámetros recibidos en la petición, ya sea por GET o POST. Los valores POST prevalecerán sobre los de GET en caso de ser idénticos. -
.files
: Un objeto que contiene únicamente los archivos, si existiesen, que han sido enviados en la petición. En.params
también podremos encontrar dichos archivos junto con el resto de parámetros que fueron enviados. -
.session
: Un objeto que mantendrá sus propiedades entre distintas peticiones del mismo cliente. -
.referer
: Si la solicitud se ha realizado por medio de un link, este valor nos indica la dirección que nos ha referenciado.
Puedes encontrar una descripción más amplia y el resto de propiedades en la referencia.
-
.setHeader(name, value)
: Añade un encabezado a la respuesta.getHeader(name)
yremoveHeader(name)
, nos permite leer un valor de encabezado ya existente o eliminarlo. Como estos, se incluyen alias de otros métodos propios deresponse
. -
.setCookie(name, value, config)
: Prepara una cookie para adjuntar a la respuesta, será encriptada por medio de JSON.crypt. -
.send(data)
: Envía el contenido especificado al cliente y termina la petición, si data es un objeto se convertirá en JSON y la respuesta se enviará como 'aplication/json', si data es un string será enviado como 'text/html'. Existen, además, métodos explícitos para cada tipo de respuesta.html()
,.json
,.text
. -
.file(path, clientname, download)
: Envía un archivo al cliente, medianteclientname
podremos especificar un nombre que será utilizado en el diálogo de guardado y condownload=true
podremos forzar la descarga del mismo. Por defecto el nombre será el del fichero original, y forzar descarga es false. Adicionalmente, el archivo será comprimido antes de su envío y quedará cacheado en memoria para sucesivas peticiones, este método es compatible con la negociación byte-serving de HTTP, por lo que si la descarga es interrumpida podrá continuar en una siguiente solicitud desde el último byte recibido, siendo también utilizado por clientes de video para descargar selectivamente la porción de video solicitada. Este método también tiene gestión de caché automática, por lo que si el cliente ya tiene el archivo no volverá a ser enviado y se responderá con un código 304. -
.cacheck(hash)
: Nos permite gestionar la caché del cliente, debemos pasar valor identificativo (hash, checksum, timestamp) para comprobar si el contenido solicitado es idéntico al que ya tiene el cliente, este método responderá con false si el cliente no tiene cacheado el contenido y true junto con un 304 automáticamente si el cliente ya dispone del contenido. La forma ideal de utilizar este método es como condición en una sentencia if, de la formaif(!cacheck(hash)){}
. -
.authenticate(msg)
: Envía un mensaje de autenticación HTTP básica con el código 401. Podremos comprobar las credenciales del usuario en la propiedad.auth
de Gangway, este objeto contendrá el nombre de usuario y contraseña indicados por el cliente en las propiedades.user
y.pass
. -
.redirect(location)
: Redirige la solicitud a otra dirección mediante un código 301. -
.render(template, locals)
: Definiendo la ruta a un template y la variables de entorno con las que debe trabajar enviaremos al cliente el resultado como HTML. Pillars.js permite añadir motores de plantillas mediantetemplated.addEngine(ext,compiler)
. -
.error(code, data)
: Mediante este método podemos mostrar mensajes de error manualmente, nos permite especificar un status-code (404, 500...) y un objeto Error si fuese necesario, el objeto Error no se mostrará al cliente si la variable de entornoproject.config.debug
es false.
Los plugins te permiten crear controladores globales que podrán acceder a las propiedades de Route, ampliar las funciones de Gangway o modificar el comportamiento general del framework.
Son la parte central de la gestión de solicitudes en Pillars.js. Cuando se recibe una solicitud HTTP se instancia un nuevo objeto Gangway, éste es enviado a una cadena de plugins organizados que irán pasando el control de dicho Gangway al siguiente Plugin.
La construcción de un plugin es muy similar a la de un Route, es necesario especificar unas propiedades y un manejador. Los plugins solo tienen dos propiedades, .id
y .active
:
myPlugin = new Plugin({
id: 'paramsLog'
}, function(gw,next){
console.log(gw.params);
next();
});
project.plugins.add(myPlugin);
Este plugin de ejemplo envía a la consola los parámetros de cualquier solicitud recibida y pasa el control al siguiente plugin. Plugin a diferencia de Route recibe dos parámetros en el manejador 'gw' y 'next', next es un método que nos permite pasar el control al siguiente Plugin.
Uno de los Plugins integrados, y más importante, es 'router.js' que decide qué objeto Route es el encargado de atender una solicitud concreta. Cuando el control llega al Plugin 'router.js' este busca el objecto route adecuado para atender la solicitud. Si no encuentra un objeto route adecuado para atender la solicitud responde un 404 y detiene la cadena de plugins. En caso de encontrar uno, guarda la referencia en gw.routing
y continua la ejecución de la cadena de plugins. Al finalizar la cadena de plugins se pasa el control al handler del objecto route seleccionado.
Desde cualquier Plugin con prioridad posterior a 'router.js' se tendrá acceso a la propiedad gw.routing
que tiene el siguiente formato:
-
gw.routing.routes
: Cadena de Routes que se ha seguido si estos han sido anidados, en orden de descendencia. -
gw.routing.handlers
: Manejador o manejadores del Route que responderá a la ruta. Serán llamados al finalizar la cadena de Plugins. -
gw.routing.inheritance
: Objeto con las propiedades del Route que atenderá la solicitud, propias y heredadas de otros Routes superiores. -
gw.routing.check(prop, preset)
: Utilidad para comprobar propiedades del Route pudiendo recibir el valorpreset
en caso de no estar definida.
Los Plugins junto con la ayuda de la propiedad gw.routing
, nos permiten crear Plugins que sólo se ejecutarán ante configuraciones concretas de un objeto route, por ejemplo un Plugin que realize log como el ejemplo anterior pero sólo para objetos Route que tengan la propiedad 'log=true':
myPlugin = new Plugin({
id: 'logPlugin'
},function(gw,next){
var log = gw.routing.check('log');
if(log===true){
console.log(gw.path,gw.params);
}
next();
});
project.plugins.add(myPlugin);
Mediante Plugins podemos actuar tanto antes como después de pasar por el plugin 'router.js', pudiendo por ejemplo modificar la ruta o métodos de la solicitud antes de dicho plugin, o comprobar propiedades del objeto Route cuando éste ya ha sido seleccionado. Cada Plugin puede modificar o extender las propiedades y métodos de Gangway, por ejemplo el Plugin 'sessions' nos ofrece la propiedad gw.session
en la cual se encarga de insertar los valores almacenados en la sesión y guardar los cambios una vez finalizada la solicitud.
Los Plugins ofrecen una forma de añadir funcionalidades a Pillars.js o modificar su comportamiento general, esto es útil para crear Plugins genéricos que añadan funciones a cualquier proyecto o para realizar tareas comunes entre diferentes manejadores pudiendo disparar el Plugin sólo ante ciertas propiedades/configuraciones de Route.