Introducción a Express (parte 2)

Introducción a Express (parte 2)


Ya estamos con la segunda entrega, espero que se os haya echo corta. Hoy voy a tratar de a hablar de routes o controladores que en express permiten vincular la lógica encargada de gestionar el comportamiento de una pantalla a un patrón de URL determinado.

En mi anterior post ya hice una breve introducción a los routes que quizas fue demasiado breve por lo que hoy vamos a hablar de otras de sus funcionalidades además de las básicas.

Lo básico

Todos los routes suelen almacenarse bajo el directorio routes en fichero que contienen una o varias funciones referenciadas en el objeto exports; en ejemplos anteriores:

// exports gives us access to index function
exports.index = function(req, res){  
res.render('index', { title: 'Express' });  
};

Seguro que alguien sigue haciendose la pregunta ¿Por que es necesario referenciar estas funciones en el objeto exports?¿Y para que?, la respuesta es sencilla, en node las funciones de los ficheros que han sido incluidos son inaccesibles hasta que han sido referenciados dentro de exports (documentación oficial)

Cualquier función encargada de manejar una ruta especifica de nuestra aplicación tiene un aspecto parecido a este:

exports.myRoute = function(request, response, next){};  
//                            1         2       3

El formato de esta función según el orden de aparición es:

  • Objeto request con todos los datos de la petición.
  • Objeto response con las funciones y valores necesarios para gestionar la petición.
  • Función next, opcional y muy util cuando usamos las funcionalidades del middleware.

Una vez tenemos nuestra función para gestionar nuestro route basta con vincularla a una ruta especifica. Express dispone de varias funciones asociadas a cada uno de los métodos HTTP con los que podemos vincular una ruta URL especifica a una función que se encargue de gestionar su lógica. Alguna de estas funciones son:

  • get: para peticiones a recursos via URL.
  • post: para peticiones en las que se remite información a un recurso.
  • all: gestiona cualquier petición.

Para vincular la ruta a nuestra función myRoute bastaria con incluir nuestro fichero y hacer uso de una de las funciones anteriores, como por ejemplo:

var routeFunction  = require("./routes/routeFunction"); // routes/routeFunction.js

// ...

app.get('/myroute', routeFunction.myRoute );  
//   1      2                  3          

En este ejemplo tenemos por orden de aparición:

  • Función que gestiona el método HTTP
  • Patrón URL que será manejado
  • Función que será ejecutada cuando se cumpla el patrón

El funcionamiento sería "Ejecuta la función myRoute dentro de routeFunction cuando se acceda a la URL /myroute". ¿Fácil no?

Patrones y parámetros

Ya sabemos vincular una función a una determinada ruta pero ¿Que es un parámetro? y ¿Como recogemos los parámetros?. Al principio cualquiera puede pensar que un parámetro es un valor que se paso tipicamente por el query string de la URL como "myRoute?myParameter=Hello+World" pero no es lo mismo (luego veremos como obtener estos).

Un parámetro es todo aquel valor que esta descrito en nuestro patrón. Para representar esto en un patrón agregamos dos puntos seguidos del nombre del parámetro:

app.get("/post/:id/mode/:mode", function (request, response, next){  
// printing request parameter
    console.log(request.params.id,request.params.mode);
    response.render("index",{});
});

En este ejemplo disponemos de un patron de URL con dos parámetros :id y :mode. Para acceder a los valores de los mismos hacemos uso del objeto JSON request.params que contiene cada uno de los parámetros. Con una URL:

http://.../post/1/mode/edit  

Obtendríamos como resultado:

1 edit  

Express también dispone de otro método para obtener los valores de los parámetros y tratarlos de manera independiente, haciendo use de la función param (disponible en el objeto app). Esta función es útil si queremos realizar algún chequeo previo a la ejecución de alguna de las rutas.

//         1        2      2.1       2.2    2.3  2.4
app.param("id", function(request, response, next, id){  
// we check our id value using a function
if(myCheckerFunction(id)){  
    // we call next to continuing with execution to /post/:id route 
    next();
}else{
    next(new Error('Incorrect id'));
}
});

// ...

app.get("/post/:id", function(request, response){  
    ...
});

Param esta compuesta por:

  • Nombre del parámetro que trata
  • Función que trata el parámetro
    • Objeto request
    • Objeto response
    • Función next
    • Variable que representa y contiene el valor esperado

En el ejemplo tratamos de validar el valor obtenido del parámetro id. Si todo va bien usamos next para permitir que continue con la ejecución en caso contrario usamos next para lanzar un error y dejar que el gestor de errores lo trate.

Parámetros query y body

Ante la eterna pregunta ¿Como recupero mis parámetros de la query string?¿Y los de mis formularios?. La respuesta es sencilla:

/*
<form action="/post?id=2">
    <input tpye="text" name="title" />
    <input type="submit" value="send" />
</form>
*/

export.queryStringTest = function(request, response){    console.log(request.query.id,request.body.title);  
    response.render("index", {});
}

Como puede verse en este ejemplo el objeto request contiene:

  • query para los parámetros remitidos por GET
  • body para los parámetros remitidos por POST

Next (Middleware)

Como ya he comentado en otras ocasiones Express esta montado sobre Connect. Todas las funciones usadas para configurar una ruta (por ejemplo get) ofrecen la posibilidad de asociar más de una función a una ruta:

// 2 options
// first, any amount of functions 
app.get("/myroute1", function(request,response,next){  
    console.log("first");
    next();
}, function(request, response){
    console.log("second");
});

// second, an array with functions
app.get("/myroute2", [  
function(request,response,next){  
    console.log("first");
    next();
},function(request, response){
    console.log("second");
}]);

Si tras la ejecución de nuestra primera función (como se observa en el ejemplo) usamos next para continuar con la segunda función. De no usarse la ejecución quedaría bloqueda en la primera función.

Un ejemplo de su aplicación sería chequear que un usuario dispone de una sesión activa, por ejemplo:

// post management after get
exports.get("/my/admin/url", function(request, response, next){  
    if(request.session.user){
        next();
    }else{
        response.status(403).redirect("/login");
    }
}, function(request, response){
    response.render("myAdminPage",{});
});

Como se puede observar en el ejemplo, verificamos la existencía de user en session en caso afirmativo continuamos con la siguiente función y en otro caso redirigimos hacia login. ¿Facíl no?.

Bueno por el momento lo dejaré aquí y en la siguiente entrega mostraré algunos ejemplos de como configurar la carga de los routes en nuestra aplicación de una forma un poco más fácil y modular... tranquilos, la proxima vez no haré esperar tanto