En el comienzo de Internet la experiencia de visitar un sitio web consistía básicamente en el consumo de información en formato texto salpicada con algunas imágenes, habitualmente en resolución relativamente baja. Una experiencia que se extendía cada vez que hacíamos click en un enlace que, mágicamente, nos transportaba a un nuevo contenido con aproximadamente el mismo reparto de ingredientes.
Con la llegada de la denominada web 2.0 se produce un gran cambio. Mientras visitábamos una página desde nuestro navegador (lo que denominamos un cliente dentro de la arquitectura cliente/servidor) el contenido de la página podía cambiar sin que tuviéramos que cargar una nueva página HTML. Mediante AJAX (Asynchronous JavaScript y XML) la página era capaz de establecer un intercambio de datos con el servidor que permitía refrescar el contenido de la página y ofrecernos nueva información tener que cargar un nuevo documento HTML con todos sus elementos.
AJAX supuso un cambio muy relevante en la experiencia de visitar un contenido web al establecer un nuevo protocolo (HMLHttpRequest) para el intercambio de datos entre el navegador (cliente) y el servidor . En este proceso se mandaba un fichero de datos XML al servidor solicitando información y los servicios web del servidor devolvían una respuesta compuesta por datos que no estaba destinada a ser consumida directamente por el usuario.
En su forma más sencilla podemos decir que un servicio web (Web Service) es un framework para que dos ordenadores puedan intercambiar datos, una comunicación que tiene lugar usando los protocolos de comunicación de la Web, en su inmensa mayoría mediante el uso de HTTP (Hypertext Transport Protocol) aunque éste no es el único protocolo de comunicación que puede usar un servicio web.
Cuando mandamos una petición (request) a un servicio web necesitamos conocer si tenemos que usar un lenguaje determinado como SOAP o JSON, si la petición se tiene que realizar en HTTP PUT o GET… lo que no necesitamos conocer es que hay al otro lado, es decir, que tipo de base de datos o que tecnología son las que utiliza el servidor. Una de las barreras que ha derribado este modelo de comunicación de datos son los problemas de interconexión entre el clientes y servidores que usan distintas tecnologías, lo servicios web han creado un lenguaje común para que ambas partes puedns usar para entenderse.
Introducción a REST
REST (Representational State Transfer) se basa en una arquitectura de datos, así como una metodología de diseño, que nos permite esperar unos resultados consistentes y predecibles cuando enviamos una petición para acceder a un recurso que se encuentra alojado en un servidor, usando una serie de métodos estandarizados y del que podremos recuperar una representación en distintos formatos de datos tales como JSON o XML. En las próximas lineas intentaré dar sentido a esta definición y a cada uno de sus componentes.
Hagamos una comparación con el proceso de recuperar una página web (un recurso en definitiva) alojada en un servidor web. Cuando escribimos la URL (la dirección que apunta a donde está alojado ese recurso) normalmente el servidor nos devuelve varias cosas:
- Un documento HTML y una serie de elementos como, por ejemplo, las imágenes
- Una hoja de estilos CSS que le indicará al navegador como debe mostrar los diferentes elementos de la página.
- Probablemente algunos elementos JavaScript (JS) que definirá como puede interactuar el usuario con ciertos elementos de la página.
Si quisiéramos cargar otra página el proceso sería muy similar. Este proceso es el que seguimos cada día al visitar cualquier sitio web, ¿Cual es el problema? Sencillamente que el uso de recursos y ancho de banda es bastante intenso.
Supongamos ahora que en lugar de bajar todos esos elementos lo que se carga en nuestro navegador es una aplicación web (One Page Application) que va a correr en nuestro navegador, y que va a estar poblada por una serie de datos iniciales que hemos descargado junto a la aplicación. En este caso lo que veamos en la pantalla es simplemente una visualización del estado de dicha aplicación en ese momento con esos datos que hemos cargado.
En esta primera carga el uso de recursos va a ser elevado también ya que inicialmente tiene que descargar algo muy similar al caso de una página estática: el framework HTML, algunas hojas de estilo y algunos elementos JavaScript. A partir de ese momento la aplicación sólo tendrá que mandar la URI ( Uniform Resource Identifier, otra forma de indicar la dirección donde están alojados los recursos que buscamos) que nos devolverá un nuevo estado, es decir una nueva carga de datos a partir de la que se actualizará el contenido de lo que el usuario ve en su pantalla. La gran diferencia radica en que lo que viaja ahora no es el conjunto completo de los elementos que hay que mostrar en la pantalla del usuario, lo que viaja es sencillamente un objeto compuesto de únicamente de datos en un formato estructurado.
Este proceso tienen otra ventaja, con un sólo proceso estandarizado ahora podemos dar servicio a múltiples aplicativos (web, móvil, wearables…), diferentes clientes que pueden consumir el mismo conjunto de datos desde una misma aplicación. Este es un concepto troncal en el lenguaje particular del mundo de las APIs, los clientes (navegador web, navegador móvil, aplicación móvil…) consumen la API, es decir realizan peticiones a la API y reciben respuestas de ésta que son capaces de procesar o simplemente consumir.
Toda esta comunicación que se produce de ida y vuelta se controla desde una API (Application Programming Interface). Una API es un conjunto de funcionalidades y reglas que se construyen sobre una pieza de software que se encarga de permitir la interacción entre dicho software y otros elementos externos. En el caso de los servicios REST las APIs son las herramientas que usamos para acceder y trabajar con los recursos REST usando una serie de métodos o verbos (GET, POST, DELETE…) que indican lo que queremos hacer y que tendrán que estar soportados por la API. A la API le da igual quién sea el cliente siempre y cuando éste siga las reglas y protocolos establecidos en la API.
Cuando hablamos de recursos tenemos que hacerlo en el contexto del mundo de la programación. En este contexto un recurso es sencillamente cualquier componente físico o virtual, dentro de un sistema. Dentro de los recursos virtuales tenemos los ficheros de datos que en términos generales es a lo que hacemos referencia en el mundo de los servicios web aunque no son el único tipo de recurso que se puede gestionar mediante este tipo de servicios.
Para referirnos a un recurso tenemos que hacerlo por su identificador, un nombre compuesto por una serie de caracteres que sirven para describirlo, y eso es precisamente lo que es una URI (Uniform Resource Identifier). De los diferentes identificadores dentro de URI los que usamos más habitualmente son las URLs (Uniform Resource Locator). Una URL es el medio por el que accedemos a una representaciónde un recurso especificando cómo acceder y donde está ubicado en la red. Por ejemplo, http://errequeerre.es/sobre-mamel-redondo
hace referencia a un recurso identificado por /sobre-mamel-redondo
cuya representación en formato HTML, con su código correspondiente, se puede obtener a través del protocolo HTTP
de un host en la red cuyo nombre de dominio es errequeerre.es
Es importante entender que los recursos pueden ser únicos, como en el ejemplo anterior, o pueden ser una colección de elementos diferentes. Por ejemplo http://errequeerre.es/category/big-data
nos da acceso a todos los post del blog (una colección de posts) que están dentro de la categoría Big Data
Otro aspecto básico y no tan intuitivo es el concepto de representacion. En un servicio REST lo que nos devuelve el servidor cuando le hacemos una petición GET (por favor dame estos datos de este recurso) no es el recurso en sí sino lo que se denomina una representación del estado actual de un recurso. ¿Y qué quiere decir el estado actual de un recurso? Básicamente se refiere a los datos de ese recurso en el momento en que los solicitamos. Por lo tanto al reclamar un recurso de un servicio podemos obtener la representación de ese recurso en diferentes formatos de datos como JSON o XML.
Veamos un ejemplo con una de las APIs de Google Maps. Vamos a realizar una petición usando el método POST y usa la URI https://maps.googleapis.com/maps/api/geocode/json?address=40+Paseo+Imperial,+Madrid&key=claveAPI donde address
incluye los parámetros de nuestra búsqueda, en este caso la dirección de las oficinas de MAKRO en Madrid y el parámetro key
incluirá la clave que proporciona Google para acceder a la API. Sin ese parámetro sólo se puede hacer una petición a la API, a partir de esa primera petición Google bloqueará cualquier otra petición por un día. Nótese que al finalizar la URL se puede ver /JSON que indica el formato de respuesta que esperamos nos devuelva la API.
A continuación el pantallazo muestra la respuesta a nuestra petición en formato JSON
Para que se vea mejor el JSON de respuesta expongo el código a continuación, es fácil comprobar que no hace falta mucha explicación para interpretar la respuesta de la API de Google Maps. Podemos ver los diferentes componentes de la respuesta organizados por address_components
y formated_address
, ésta última incluye las coordenadas de la latitud y longitud de la dirección así como la dirección completa con el formato establecido por Google (calle, número, código postal, ciudad, pais).
{ "results" : [ { "address_components" : [ { "long_name" : "40", "short_name" : "40", "types" : [ "street_number" ] }, { "long_name" : "Paseo Imperial", "short_name" : "Paseo Imperial", "types" : [ "route" ] }, { "long_name" : "Madrid", "short_name" : "Madrid", "types" : [ "locality", "political" ] }, { "long_name" : "Madrid", "short_name" : "M", "types" : [ "administrative_area_level_2", "political" ] }, { "long_name" : "Comunidad de Madrid", "short_name" : "Comunidad de Madrid", "types" : [ "administrative_area_level_1", "political" ] }, { "long_name" : "España", "short_name" : "ES", "types" : [ "country", "political" ] }, { "long_name" : "28005", "short_name" : "28005", "types" : [ "postal_code" ] } ], "formatted_address" : "Paseo Imperial, 40, 28005 Madrid, España", "geometry" : { "location" : { "lat" : 40.4022096, "lng" : -3.715519699999999 }, "location_type" : "ROOFTOP", "viewport" : { "northeast" : { "lat" : 40.4035585802915, "lng" : -3.714170719708497 }, "southwest" : { "lat" : 40.4008606197085, "lng" : -3.716868680291502 } } }, "place_id" : "ChIJg7cq1donQg0RHx74s9JojNU", "types" : [ "street_address" ] } ], "status" : "OK" }
Si cambiamos nuestra petición para que el API nos devuelva la respuesta en XML obtendremos los mismos datos en el formato especificado:
<?xml version="1.0" encoding="UTF-8" ?> <GeocodeResponse> <status>OK</status> <result> <type>street_address</type> <formatted_address>Paseo Imperial, 40, 28005 Madrid, España</formatted_address> <address_component> <long_name>40</long_name> <short_name>40</short_name> <type>street_number</type> </address_component> <address_component> <long_name>Paseo Imperial</long_name> <short_name>Paseo Imperial</short_name> <type>route</type> </address_component> <address_component> <long_name>Madrid</long_name> <short_name>Madrid</short_name> <type>locality</type> <type>political</type> </address_component> <address_component> <long_name>Madrid</long_name> <short_name>M</short_name> <type>administrative_area_level_2</type> <type>political</type> </address_component> <address_component> <long_name>Comunidad de Madrid</long_name> <short_name>Comunidad de Madrid</short_name> <type>administrative_area_level_1</type> <type>political</type> </address_component> <address_component> <long_name>España</long_name> <short_name>ES</short_name> <type>country</type> <type>political</type> </address_component> <address_component> <long_name>28005</long_name> <short_name>28005</short_name> <type>postal_code</type> </address_component> <geometry> <location> <lat>40.4022096</lat> <lng>-3.7155197</lng> </location> <location_type>ROOFTOP</location_type> <viewport> <southwest>…</southwest> <northeast>…</northeast> </viewport> </geometry> <place_id>ChIJg7cq1donQg0RHx74s9JojNU</place_id> </result> </GeocodeResponse>
De manera sencilla hemos visto como podemos obtener dos representaciones del mismo recurso una en XML y la otra en JSON. Se puede comprobar como JSON es mucho menos verboso que XML, que básicamente quiere decir que es capaz de expresar lo mismo usando menos Información. Fundamentalmente XML requiere abrir y cerrar una etiqueta para cada uno de los componentes mientras que JSON sólo requiere declarar el objeto e indicar su valor en un formato que se denomina pares de nombre/valor. Ambos formatos son jerárquicos, es decir tienen una estructura anidada de padres e hijos.
Este ahorro de caracteres que muestra JSON frente a XML es uno de los motivos por los que ha ido ganando popularidad ya que su consumo de recursos es menor que el del XML y, especialmente en el mundo de internet en el móvil, cada carácter cuenta. Otra ventaja del JSON es que al ser un objeto JavaScript es mucho más fácil de consumir por nuestro navegador que el XML.
Próximo articulo de la serie: Anatomía de una API RESTful