Archivo de la etiqueta: URL

Crear un servidor web con Node.js (II)

En el anterior capítulo nos quedamos con el servidor Node montado y corriendo. Lo próximo es construir una aplicación más compleja que responda a varias URLs, para ver en movimiento algo de lo que sería REST. Evidentemente, debemos examinar la petición HTTP, extrayendo de ella la URL pedida y, ocasionalmente, sus parámetros GET/POST.

La idea es que para procesar las peticiones necesitamos una especie de router que, dependiendo de la URL, mapee a los distintos manejadores, y en un rato veremos el cómo. En este punto hace falta aclarar un pequeño detalle:

Si comparamos Node con, por ejemplo, un Apache+PHP, hay una diferencia principal: el código PHP contiene la lógica, accesos a base de datos, etc.; es decir, implementa la aplicación, pero es Apache quien se encarga de servirla y gestionar las peticiones. Node realiza las funciones de ambos, no sólo implementamos la aplicación sino también todo el servidor, de forma que la aplicación web y su servidor web son esencialmente lo mismo.

Callbacks y funciones anónimas

Bien, ya vimos la función principal en nuestro hello.js, que crea el servidor y escucha peticiones en el puerto indicado. Al crear el servidor le estamos pasando una función como parámetro, lo cual se puede hacer en JavaScript incluso sin crear anteriormente la función. Es decir, se puede asignar una función a una variable y después pasar esta como parámetro, o bien definir la función en el mismo parámetro y por tanto no hay que darle un nombre (función anónima).

Este tipo de comportamiento se debe a la naturaleza de Node.js y a que trabaja orientado al evento. De esta forma, pasando una función al método que crea el servidor, cada vez que se reciba una petición, esta función será llamada (callback). Así que manipulamos ahí mismo la petición entrante, justo donde se captura el evento.

La estructura de la URL

Por otra parte, a la función que responde al evento (manejador) se le pasan dos objetos, request y response, con cuyas propiedades podemos obtener los detalles de la petición y procesarla adecuadamente.

Repasemos por un momento la estructura de las URLs y las partes que nos pueden interesar. En esencia, una URL puede tener unas cuantas de estas cosas (algunas de ellas están siempre, aunque no las veamos en el navegador, como las meigas):

URL parts

Brevemente:

  • Protocolo: normalmente HTTP, pero también HTTPS, FTP, etc.
  • Host/Hostname: dominio, es decir, nombre o dirección IP de la máquina.
  • Puerto: puerto de red en el que escucha (por defecto 80 en HTTP).
  • Path: ruta en la que se localiza el recurso.
  • Query: parámetros de la petición: pares de variable=valor.

Obviamente, las partes importantes aquí son el path y la query: queremos saber a qué recurso se ha accedido, y si vienen datos con ello. Los obtenemos de la siguiente manera (usando los módulos internos url y querystring):

var url = require(‘url’);
var querystring = require(‘querystring’);

// Obtener el path o la query como cadena
var path = url.parse(request.url).pathname;  // “/hola”
var query = url.parse(request.url).query;  // “nombre=Pepito&apellido=Pérez”
// Obtener el objeto JSON a partir de la cadena query
var params = querystring.parse(query);  // { nombre: Pepito, apellido: Pérez }

A través del objeto request también podemos obtener otras cosas interesantes, como protocolo y versión, método, cabeceras…

Módulos: require y exports

Para dotar a nuestra aplicación de cierta organización, podemos separar todo el código en módulos. Esencialmente, un módulo es un fichero que agrupa ciertas funcionalidades, y que se puede usar posteriormente desde otro fichero.

Vemos en los ejemplos algunas líneas con require. Dado que ‘http’, ‘url’ o ‘querystring’ son módulos (internos de Node.js en este caso), require carga las funcionalidades que dichos módulos proporcionan (exportan).

Ahora nos queda crear nuestros propios módulos, y despues utilizarlos. Dentro de un módulo, podemos elegir qué partes queremos exportar, ya que todo lo que haya dentro es por defecto privado (variables, funciones, clases…). Y de hecho, la palabra que se usa para ello es exports.

Lo explican genial aquí, por lo que me limito a decir que lo haríamos de una de las dos formas:

a)     Exportando los componentes por separado:

var num = 35;
var sumar = function(a, b) {
    return a+b;
}
module.exports.num = num;
module.exports.sumar = sumar;
--------------------------------
var utils = require(‘./modulo);
var x = utils.num + 2;
var res = utils.sumar (4,9);

b)     Exportando todo en una función:

var miClase = function(param1, param2) {
   this.param1 = param1;
   this.param2 = param2;
}
module.exports = miClase;
--------------------------------
var clase = require(‘./miClase’);
var obj = new clase();
// O también:
var obj = new require(‘./miClase’)();

Mezclar y agitar

Muy bien, ya tenemos todos los ingredientes: callbacks y funciones anónimas, partes interesantes de la URL y nuestros propios módulos.

Ya podemos hacernos nuestro router casero, y poder decirle hola y adiós al usuario (un ejemplo muy básico para entender cómo se mapean las peticiones).

Para que no haya confusión, voy a renombrar mi hello.js a app.js (nombre más coherente), y voy a crear también router.js, que será usado en la aplicación principal.

function route(pathname, query) {
    switch (pathname) {
        case "/hola":
            return "Hello " + query["nombre"] + " " + query["apellido"]
        case "/adios":
            return "Bye bye " + query["nombre"] + " " + query["apellido"]
        default:
            return "Este recurso no existe!"
    }
}
exports.route = route;
var http = require('http');
var url = require('url');
var querystring = require('querystring');
var router = require('./router');

http.createServer(function (request, response) {
    res.writeHead(200, {"Content-Type'": "text/html"});

    var pathname = url.parse(req.url).pathname;
    var query = url.parse(req.url).query;
    var params = querystring.parse(query);

    var response = router.route(pathname, params);
    res.write(response);
    res.end();
}).listen(5555);

Claro que se podría hacer algo más elaborado, como tener en cuenta el método (GET/POST) y hacer cosas diferentes con uno y otro, pasarle al router también el objeto response y devolver una página 404 para los recursos que no existan, o bien cambiar el switch y organizar los recursos pedidos de otra manera…

Para todo esto existe una librería que veremos el próximo día: Expressjs 🙂

Saludos!

Deja un comentario

Archivado bajo Tecnologías, Tutoriales

Servicios Web RESTful

REST es el acrónimo de REpresentational State Transfer, o Transferencia de Estado REpresentacional. Describe un estilo arquitectural para diseñar aplicaciones en red, y se presentó por primera vez en el año 2000. La idea es utilizar simplemente HTTP para realizar llamadas entre máquinas, en lugar de otros mecanismos o protocolos más complejos.

rest-2

Pero, ¿en qué consiste? ¿Por qué se llama así?

El concepto más importante en REST es la existencia de recursos (elementos de información de los que se compone la Web), que pueden ser accedidos utilizando un identificador (URI). Para manipular estos recursos, los clientes y servidores de la red se comunican a través de una interfaz estándar (HTTP en este caso) e intercambian representaciones de estos.

Cuando se realizan peticiones a través de las URLs, se devuelve una representación de esos recursos, la cual deja la aplicación en un estado. Otras acciones (pedir otra URL, navegar a través de un enlace), hacen que se devuelva otro recurso, y esta nueva representación cambia de estado nuevamente la aplicación. De esta forma, con cada representación de los recursos, la aplicación cambia (transfiere) de estado.

Por lo tanto, REST define un conjunto de principios arquitectónicos para diseñar servicios web en los que los protagonistas son los recursos, incluyendo cómo se accede a su estado y cómo se transfieren a los clientes.

¿Estándar?

Ojo, REST no es un estándar con una especificación definida por el W3C. Tampoco se encarga de los detalles de implementación del servidor (es indiferente que el servicio web esté construido con PHP, servlets de Java, CGI… un servicio REST es independiente del lenguaje y de la plataforma). Es sólo un estilo que se puede seguir para diseñar un servicio web. Sin embargo, sí se basa en estándares:

  • HTTP
  • URL
  • XML/HTML/GIF/JPEG/etc (representaciones de los recursos)
  • text/xml, text/html, image/gif, image/jpeg, etc (Tipos MIME)

REST por sí solo tampoco ofrece características de seguridad, encriptación, gestión de sesiones… Por supuesto, y como en cualquier servicio web, estas funciones se pueden añadir por encima de HTTP: mediante usuarios y contraseñas, SSL, cookies…

REST ha ido teniendo una amplia acogida en toda la web y en la comunidad de desarrolladores, ya que es un modelo más fácil de usar, orientado a los recursos.

Los sistemas que siguen este esquema se denominan RESTful.

rest-1

¿Y cuáles son esos principios?

Como decíamos, REST describe los fundamentos que hacen posible que la Web funcione bien y sea altamente escalable. Estos diseños fundamentales son:

  • No se mantiene el estado: cada petición HTTP contiene toda la información necesaria para ser comprendida. Como resultado, ni el cliente ni el servidor necesitan recordar ningún estado de las comunicaciones entre mensajes. De esta forma, se mejora el rendimiento de los servicios y se hace más simple su diseño e implementación. En la práctica, sabemos que HTTP no mantiene estado, por ello muchas aplicaciones utilizan cookies y otros mecanismos para mantener la sesión (algunas de estas prácticas no son permitidas por REST).
  • Un conjunto de operaciones bien definidas que se aplican a los recursos de forma inequívoca. En el caso de HTTP existen unas operaciones, que deben usarse de forma consecuente con la definición del protocolo:
  • GET: para obtener un recurso. Debe ser idempotente.
  • POST: para crear un recurso.
  • PUT: para modificar un recurso o cambiar su estado.
  • DELETE: para eliminar un recurso.
  • Una sintaxis universal para identificar los recursos. En un sistema REST, cada recurso es direccionable únicamente a través de su URI. Estas URIs se definen de forma análoga a una estructura basada en directorios, de forma que la jerarquía es fácilmente comprensible y necesita escasa documentación.
  • El formato de datos que el servicio utiliza en las peticiones y respuestas: la representación del estado de un recurso en un sistema REST es típicamente HTML o XML (a veces JSON), donde la estructura devuelta podría corresponderse con un registro de la base de datos, y el contenido de las etiquetas mostrase los valores de las filas. Esto permite que el servicio sea utilizado en diferentes plataformas, lenguajes y dispositivos.

URLs lógicas y URLs físicas

Un recurso es una entidad, un concepto. Una representación de este recurso es una manifestación concreta de éste. Cuando accedemos, por ejemplo, a la URL

http://www.misitio.com/persona/5897

se trata de una URL lógica, no física. Obviamente, no existe una página HTML estática para cada una de las personas en ese site, ni una carpeta anidada por cada nivel en la URL. De ser así, el diseño no sería muy escalable precisamente.

Simplemente, la URL contiene toda la información necesaria para acceder al recurso; el primer paso natural es el de ‘parsear’ la petición, mientras que los detalles de implementación del servidor quedan ocultos: puede tratarse, por ejemplo, de una consulta de base de datos en una tabla ‘personas’ cuyo id sea el 5897, etc.

La cuestión es que las URLs no revelan ningún tipo de información sobre los ficheros existentes, ni tampoco sobre la implementación. Ésta se debe poder cambiar sin que ello afecte a la estructura de las URLs en la página.

En próximos episodios veremos cómo podemos construir desde cero, y de una manera muy sencilla nuestro servidor web con una API REST utilizando… bueno, no os lo cuento. ¿Dejamos la incógnita unos días?

1 comentario

Archivado bajo Tecnologías