viernes, 9 de marzo de 2012

Arquitectura REST para servicios web (con ejemplo en jQuery!)

Hola a todos!

En esta ocasión les platicaré un poco acerca de REST, una arquitectura para diseñar y construir servicios web. Este tema fué escogido debido a que fuera de los ámbitos de los desarrolladores, rara vez uno escucha los términos "arquitectura" al hablar de programación. No es lo mismo hablar de arquitectura de servicios como de  patrones de programación, o patrones de diseño. En próximas entregas hablaré un poco acerca de estos últimos.

Entonces, comencemos. Las siglas REST vienen de Representational State Transfer, y el término per se fué acuñado por Roy Fielding en su disertación de doctorado. Para no adentrarnos en los específicos de él, trataremos cómo implementarlo y por qué se ha vuelto tan popular desde su introducción.

REST plantea una nueva manera de modelar los servicios distribuidos, es decir, aquellos servicios que operan a través de una red, ya sea que se tengan recursos diferentes en máquinas diferentes, o que se trate de un servidor central y cierta cantidad de clientes. Históricamente, utilizando SOAP o algún otro tipo de servicios, se hacía lo siguiente:

Digamos que tenemos un blog, y queremos diseñar la interfaz del servicio al mundo exterior. Primeramente, definiríamos las acciones que se podrían llevar a cabo, por poner algunas:

  • getPostsList($tag, $category)
  • getUserPosts($userId)
  • getPostComments($postId)
en adelante. ¿Qué sucede si 6 meses después de que inicia operaciones nuestro blog queremos desarrollar una aplicación móvil, o simplemente nuestra interfaz de usuario cambia? Nos veríamos obligados a seguir las reglas del contrato que realizamos al inicio del desarrollo, es decir, a utilizar las funciones que mapean las acciones posibles del servidor a cada paso, lo cual representaría una tarea ardua de mantenimiento y actualización en cada cambio. ¿Y qué tal si requerimos cambiar algo en el servidor, aunque sea la firma de alguna función? Peor aún: tenemos que recorrer todo el código que depende de tal función, cambiarlo y verificar que cumple la nueva firma. En conclusión: tal vez parezca intuitivo hacer todo así, pero a la larga causará problemas de mantenimiento y escalabilidad.

En cambio, REST propone algo un tanto extraño: utilizar la definición del HTTP al máximo, y crear interfaces que expongan las recursos del servicio por medio de métodos estándar: es decir, adiós a los getters() y setters().

Para quienes no estén tan familiarizados con HTTP, basta recordar que existen dos tipos de métodos bastante usuales: GET y POST. Sin embargo, no son esos todos los que fueron definidos en el RFC, sino que existen otros dos cuya importancia ha sido marginal puesto que hasta hace poco, ningún navegador implementaba ese tipo de peticiones. Con la llegada de HTML5 y toda la funcionalidad extra que trae consigo, la presión sobre los desarrolladores de los navegadores fué mayor y por tanto podemos encontrar que la mayoría de los navegadores ya permiten realizar peticiones de esos tipos, si bien sólo por medio de XHttpRequest, mejor conocido como AJAX. Evidentemente, el hermano relegado de estos navegadores es IE, que no tenía pensado soportar dichas peticiones.

Como su nombre lo indica, GET, POST, PUT y DELETE hacen referencia a acciones que pueden realizarse con la información, y dado que son peticiones estándar, nos hace pensar en una arquitectura que explote al máximo esta funcionalidad: REST. 

Al repensar nuestro modelo de acuerdo a la arquitectura REST, vemos que no requerimos "funciones" para cada una de las acciones posibles dentro del servicio. Asimismo, ya no requerimos pensar en cómo exponer los recursos con los que contamos, puesto que todo esto se resuelve con HTTP:

  • Primero, pensamos en los recursos como objetos, como si fueran instancias de clases.
  • Después, vemos que con una sola dirección de acces (y diferentes peticiones) podemos realizar las operaciones básicas (CRUD) con ese objeto, lo cual simplifica mucho la interfaz.
En términos prácticos, esto se refleja en la siguiente estructura del blog:
  1. Se tendrán ciertas direcciones de recursos (URI) las cuales expondrán la información de dicho objeto.
  2. Mediante el tipo de petición, el servidor sabrá qué acción desea realizarse con el objeto, la realizará y le informará del resultado mediante los códigos de status HTTP al cliente.
Entonces, podemos, después de pensar un rato, que las entidades de las que se compone el blog son, a grandes rasgos, los usuarios, posts, comentarios, y tags. Fácil es ver que se requerirían 4 direcciones en nuestro servicio:
  1. midominio.com/blog/user/x
  2. midominio.com/blog/post/x
  3. midominio.com/blog/comment/x
  4. midominio.com/blog/tag/x
Ahora bien, eso sólo cubre una parte de los requisitos. Podemos realizar las acciones CRUD en cada una de esas entidades, pero nos resta saber de qué manera interactúan entre ellas. Por ejemplo, no tiene sentido hablar de un comentario a menos que sea a través de su autor o de el post en el que fué hecho. Eso provoca que se den los siguientes cambios:
  1. midominio.com/blog/user/x
  2. midominio.com/blog/user/x/comment/x
  3. midominio.com/blog/post/x
  4. midominio.com/blog/post/x/comment/x
  5. ...
Ahora, la cuestión de las peticiones: PUT, después de leer y leer discusiones, puede considerarse correcto su uso al querer hacer algo con información ya existente, es decir, información cuyo acceso conocemos. En otras palabras, su uso se presta perfectamente a un update, puesto que para realizarlo ya conocemos la identidad de la entidad a actualizar. POST puede utilizarse para muchas cosas, usualmente procesamiento de datos. Sin embargo, en el contexto REST, es comúnmente utilizado para crear una nueva instancia. DELETE, como su nombre indica, elimina la instancia del servidor, y GET sirve para obtener información sobre ella.

Para concluir entonces, les comparto un pequeño código en jQuery que ilustra la manera de trabajar:

$.ajax({
        type: "PUT",

        url: "http://midominio.com/blog/user/"+login,
        dataType: "json",
        data : {login : login,
                pass : pass,
                },
        success: function(datos) {

          alert("Datos recibidos! "+datos.login);
          //Recibimos datos como JSON: accedemos a sus propiedades como key.value
          $("div#json_results").append(datos.login);
        }
      });
    });

Básicamente, sucede lo siguiente: se realiza una petición AJAX a nuestro servidor en la dirección .../blog/user/x, que al ser PUT, actualizaría los datos con los que le enviamos, y nos podría regresar un array de datos en JSON, donde uno de ellos es el login actualizado (para verificar que todo salió a pedir de boca).

Para el lado servidor, pueden "simular" REST utilizando las variables de entorno como $_REQUEST, o utilizar alguno de los frameworks existentes. Existen varios que permiten trabajar de esta manera mucho más cómodamente, como Symfony (PHP, mediante un front-controller) y bakbone.js (JS, MVC de javascript para el cliente). Se realizan peticiones AJAX hacia los recursos del servidor, quien en caso de requerir regresar información, lo puede hacer de manera conveniente codificada como JSON o XML.

Finalmente, les dejo varias referencias que me fueron bastante útiles a la hora de aprender a hacer cosas "RESTful", como se le denomina a todo lo que cumpla esta arquitectura.

Happy Coding!


Referencias:
















No hay comentarios:

Publicar un comentario