Tabla de Contenidos

AJAX

Módulo perteneciente al curso Programación avanzada con JavaScript y ECMAScript.

Introducción

Desde sus comienzos y hasta hace relativamente poco tiempo las interfaces de usuario de las aplicaciones Web fueron más o menos siempre iguales. Las limitaciones propias del protocolo HTTP (Hyper Text Transfer Protocol) utilizado en las páginas Web han impuesto el tradicional modelo de “Petición-Respuesta-Procesado en el navegador” (a partir de ahora PRP) y vuelta a empezar.

Los pasos que sigue una aplicación Web convencional para funcionar suelen ser los siguientes:

  1. El usuario solicita una página.
  2. El servidor devuelve el contenido HTML correspondiente a ésta, normalmente generado a partir de alguna tecnología de servidor (como ASP.NET o PHP).
  3. El navegador recibe este HTML, lo procesa y visualiza el contenido resultante.
  4. El usuario interactúa con el HTML, envía un formulario al servidor o pulsa un enlace, y se repite el ciclo desde el paso 1: se solicita la página, se devuelve su contenido, se procesa y se visualiza.

Este proceso es el más natural para HTTP que estaba pensado para funcionar así cuando se concibió. El problema es que cualquier acción en la página que requiera apoyo del servidor implica solicitar otra página completa, descargarla y procesarla, lo cual es percibido de manera evidente por los usuarios (es decir “se nota” el refresco de la página) y crea una sensación poco amigable.

Además, si el retorno de la página tarda más que unos pocos milisegundos se pierde capacidad de respuesta de la interfaz puesto que, durante el proceso de petición-respuesta-procesado, la zona de visualización del navegador no responde.

La tendencia actual en todos los desarrollos web, sin embargo, es crear aplicaciones y páginas cada vez más parecidas a aplicaciones de escritorio, desdibujando las fronteras entre la Web y los programas que se ejecutan en el equipo local. Esto implica que los molestos y a la vez inevitables viajes al servidor no deberían ser percibidos por los usuarios y que la interfaz de la página debe responder en todo momento, jamás bloquearse. La sensación para los usuarios debe ser la de que la aplicación está todo el tiempo en su equipo, dejando de lado al servidor, como en una aplicación de escritorio tradicional. Seguro que has utilizado alguna vez GMail, Outlook.com, Facebook o alguna otra aplicación web reciente, así que sabes perfectamente de qué estoy hablando.

Ya hemos aprendido a lo largo de este curso las posibilidades que ofrecen los navegadores actuales para trabajar en el lado cliente con HTML, CSS y JavaScript. La tendencia imparable en las aplicaciones web es la de llevar cada vez más procesamiento y lógica al navegador, dejando el servidor para procesar las reglas de negocio y el acceso a datos.

Dado que las páginas se recargan cada vez menos pero las aplicaciones web necesitan comunicarse con el servidor, se necesita algún mecanismo para poder hacerlo de manera transparente al usuario. Ese mecanismo se denomina de manera genérica AJAX.

Este simpático acrónimo hasta hace poco asociado con el mundo de la limpieza y el fútbol, viene del acrónimo en inglés Asynchronous JavaScript And XML. Se basa en el uso de un objeto llamado XMLHttpRequest o abreviadamente XHR que está presente en todos los navegadores modernos. Como es fácil imaginar por su nombre, sirve para realizar peticiones de documentos XML al servidor a través del protocolo HTTP. La idea original era que, utilizando este objeto se solicitan al servidor datos en formato XML que, una vez recibidos en el navegador, es posible manipular mediante código JavaScript, mostrando los resultados al modificar dinámicamente los elementos de la página. Como comprobaremos en breve, desde este punto inicial a lo que AJAX implica actualmente, las cosas han cambiado mucho.

Aunque casi todo el mundo se piensa que AJAX es un invento de Google y su potente cliente Web de correo electrónico GMail (primera aplicación que en verdad lo popularizó entre el público general), el objeto XMLHttpRequest apareció originariamente junto a las bibliotecas XML de Microsoft (MSXML) a finales de los años noventa del siglo pasado. El concepto original de esta tecnología fue creado también por Microsoft (se Llamaba Remote Scripting), el primer navegador en soportarlo fue Internet Explorer y la primera aplicación de este tipo fue Outlook Web Access (OWA), para acceder a buzones Exchange.

Como veremos enseguida, aparte de que su nombre original haya perdurado por ser simpático, la realidad es que AJAX no siempre es asíncrono y casi nunca usa XML. De hecho, lo más habitual es que hoy en día utilice otro formato para transferir los datos: JSON, que ya hemos estudiado en el módulo de programación orientada a objetos. Lo que de verdad constituye el corazón de esta técnica es el objeto XMLHttpRequest.

JSON (JavaScript Object Notation): es un formato estándar para definición de objetos en este lenguaje que es fácil de leer para humanos y fácil de interpretar para máquinas, y que utiliza parejas de clave-valor para definir los objetos. Se usa hoy en día como el principal formato de intercambio de datos entre servidores y aplicaciones web, como alternativa al más complejo y pesado XML.

En este módulo vamos a estudiar los fundamentos de la tecnología para que no dependas de biblioteca alguna a la hora de implementar estas características, y sobre todo -seamos realistas, casi nunca usarás esto “a pelo”- para que cuando uses una de dichas bibliotecas comprendas lo que hay debajo y puedas determinar posibles problemas.

Conociendo bien los conceptos subyacentes tendrás muchas más herramientas para sacarle partido a las bibliotecas construidas sobre ellos, como por ejemplo jQuery, AngularJS, EmberJS, etc…

El objeto XMLHttpRequest

Para sacar partido a AJAX, aparte de tener conocimientos de HTML y JavaScript, el primer objeto que debemos conocer a fondo es el mencionado XMLHttpRequest.

Se trata de una clase disponible en todos los navegadores modernos que permite lanzar desde JavaScript peticiones de recursos GET Y POST a través de HTTP.

Dicho en lenguaje simple, lo que esta clase nos permite es simular mediante código JavaScript llamadas al servidor como si éstas hubieran sido hechas por los usuarios. El efecto es el mismo que si el usuario hubiese enviado un formulario o pulsado sobre un enlace, sólo que nuestro código es el que tiene el control sobre ello y lo gestiona de manera paralela al contenido actual de la página (en la jerga se suele decir que lo gestiona “por debajo” o “por detrás”).

Los métodos y propiedades básicos de esta clase que debemos conocer son los siguientes (los corchetes indican parámetros opcionales):

Aunque dispone de algunos métodos y propiedades más, con estas tendremos suficiente para el 99% de los casos que nos vamos a encontrar.

Vamos a ver cómo funciona en la práctica con un ejemplo sencillo pero directo.

NOTA: este curso se centra exclusivamente en JavaScript y tecnología de lado cliente. Por ello, aunque en aplicaciones con AJAX se suele utilizar siempre algún tipo de programa de servidor que devuelve información, no es objeto de este curso aprender a crear este tipo de servicios, que requerirían realmente un curso en sí mismo. Así que deberás aprender a crear servicios usando la tecnología de servidor que prefieras: ASP.NET, ASP.NET MVC/Web API, PHP, NodeJS, Phython… También puedes utilizar alguna API de terceras partes que te proporcione la información. Veremos un ejemplo posteriormente.

DEMO: Uso básico de XMLHttpRequest

Este ejemplo está hecho en su parte servidora con ASP.

El HTML con el JavaScript:

<!DOCTYPE html>
<html>
    <head>
	<title>Ejemplo de autocompletar con AJAX</title>
	<script type="text/javascript">
 
            var httpReq;    //objeto para realizar peticiones HTTP
	    var items;	    //Contendrá el XML obtenido para los elementos de la lista
 
            //Se encarga de cargar los items de la lista secundaria
	    function cargaItems(tipo)
            {
		getXML("damesecundario.asp?tipo=" + tipo);
	    }
 
            function getXML(url)
	    {
		// Soportado por navegadores antiguos
		if (window.XMLHttpRequest)
		{
		    httpReq = new XMLHttpRequest();
		}
		else	//Internet Explorer 8 y anteriores lo expone como control Active X
		{
		    httpReq = new ActiveXObject("Microsoft.XMLHTTP");
		}
 
		//Ya debería tener una referencia al objeto
                if (httpReq != null)
		{
		    httpReq.onreadystatechange = finCarga;
		    httpReq.open("GET", url, true)	//El true del final es para que lo solicite de forma asíncrona
		    httpReq.send(null);	//al ser una petición GET no va nada en el cuerpo de la petición, de ahí el null
		}
	    }
 
	    //Función que se ejecuta cuando cambia el estado de la carga del objeto httpReq
	    function finCarga()
	    {
                if (httpReq.readyState == 4)	//4: completado, 3: en curso, 1:cargado, 0: no iniciado
		{
		    if (httpReq.status == 200)	//200: OK (código HTTP, podría haber sido 404 (no encontrado), 500 (error), etc...)
		    {
			items = httpReq.responseXML.getElementsByTagName("item");	//Coge sólo los elementos "item"
		        ProcesaItems();
		    }
		    else	//Se produjo un error
		    {
			alert("No se pudo recuperar la información de la lista secundaria: " + httpReq.statusText);
		    }
                }
	    }
 
	    function ProcesaItems()
	    {
		listaSec = document.getElementById("mnuSecundario");
		if (listaSec != null)
		{
		    listaSec.options.length = 0;	//Vacía la lista de opciones
		    //Cargamos las nuevas opciones
		    for(i=0; i<items.length; i++)
		    {
			var opc = document.createElement("OPTION");
			opc.text = items[i].childNodes[0].nodeValue;
			opc.value = items[i].childNodes[0].nodeValue;
			listaSec.options.add(opc);
		    }
		}
	    }
        </script>
    </head>
    <body>
	<a href="http://www.campusmvp.com">
	<img src="campusmvp.jpg" style="border-width: 0px" /></a>
	<p><b>Ejemplo</b> de <b>auto-completar</b> din&aacute;micamente desde el servidor
	<b>con AJAX</b></p>
	<p>Selecciona un elemento de la primera lista y ver&aacute;s como se rellena 
	<br>din&aacute;micamente el contenido de la segunda con AJAX desde el servidor:</p>
	<form name="form1">
	    <p><select name="mnuPrimario" id="mnuPrimario" size="1" onchange="cargaItems(this.value);">
	        <option value=""></option>
                <option value="revistas">Revistas</option>
                <option value="blogs">Blogs</option>
                <option value="empresas">Empresas</option>
                <option value="libros">Libros</option>
              </select>
              <select name="mnuSecundario" id="mnuSecundario" size="1">
                  <option value="">[Sin elementos]</option>
	      </select></p>
	</form>
	<p>&nbsp;</p>
    </body>
</html>

La anterior página web es un formulario con dos selectores. Lo que haces es que cuando se elige algo en el primer selector, en la segunda lista aparecerán más opciones dependiendo de lo que hayamos elegido.

Para la segunda lista de opciones, se hace una consulta usando AJAX a un script hecho en ASP.

El fichero ASP:

<%
	Dim items
	Select Case LCase(Request("tipo"))
		Case "revistas"
			items = Array("PC World", "MSDN Magazine", "CodeProject")
		Case "blogs"
			items = Array("www.campusmvp.com", "www.jasoft.org", "jmalarcon.es")
		Case "empresas"
			items = Array("Krasis [www.krasis.com]", "Microsoft [www.microsoft.com]", "Plain Concepts [www.plainconcepts.com]")
		Case "libros"
			items = Array("Crimen y castigo", "Cien años de soledad", "El Quijote")
	End Select
 
	'Se devuelve como XML
	Response.ContentType = "text/xml"
	'Se evita que haya caché de este contenido devuelto
	Response.AddHeader "cache-control", "no-cache"
	Response.Expires = -1
 
	'Se devuelve el XML
	Response.Write DevuelveMatriz(items)
 
'Esta función se encarga decrear el XML apropiado
Function DevuelveMatriz(arrItems)
	Dim sRes
	sRes = "<?xml version=""1.0"" encoding=""ISO-8859-1"" ?>" & vbCrLf & "<items>"
 
	If IsArray(arrItems) Then
		'Se recorre la matriz para construir el XML
		For Each item In arrItems
			sRes = sRes + "<item>" + item + "</item>"
		Next
	Else
		sRes = sRes + "<item>[Sin elementos]</item>"
	End If
 
	sRes = sRes + "</items>"
 
	DevuelveMatriz = sRes
End Function
%>

Envío de datos al servidor

Normalmente cuando pensamos en AJAX, es decir, en llamadas asíncronas a servicios, lo hacemos desde el punto de vista de obtener información: llamo a una página que me devuelve unos valores y los muestro en la interfaz de usuario. Es lo que acabamos de ver en el vídeo anterior.

Aunque este es el uso que primero viene a la mente para AJAX, lo cierto es que se utiliza tanto o más en el sentido inverso: para enviar datos al servidor. Con eso podemos actualizar un registro, enviar una orden, ordenar el borrado de un dato, o incluso subir un archivo.

La forma más sencilla y directa de enviar datos simples al servidor es incluirlos en la URL a la que llamamos como parámetros GET:

urldestino.aspx?Parametro1=1234&Parametro2=5

GET: Método HTTP que envía la información de un formulario en el propio URL de destino de éste, añadiéndolos al final como parámetro del “QueryString” (lo que va después de la interrogación.

Aunque esto puede servirnos para cosas muy sencillas no es lo que necesitaremos en la mayor parte de los casos.

Lo habitual es que la información haya que enviarla con el método POST. La principal diferencia entre GET y POST estriba en que el método GET hace una sola llamada al servidor, solicitando una página y enviando algunos parámetros de datos en la propia petición. POST por el contrario solicita la URL pero además envía los datos en el cuerpo de la petición, como contenido.

Por GET lo máximo que se puede enviar son 2 kB de información, mientras que por POST no existe esta limitación.

POST: Método HTTP que envía la información usando una petición en la que los datos del formulario viajan adjuntos dentro del contenido de la propia petición, por lo que no son visibles para el usuario.

Para enviar datos al servidor mediante POST nuestro código AJAX sería similar al siguiente:

http = getHttpRequest()
http.onreadystatechange = finCarga;
http.open("POST", "http://www.miserv.com/misdatos.aspx", true)
http.send('Parametro1=1234&Parametro2=5');

Con esto no hemos ganado demasiado. Ahora se envían los datos por POST (sólo cambia el primer parámetro de open) pero los hemos tenido que introducir en el método send en lugar de en la propia URL. Esto sólo simularía el envío de parámetros mediante POST desde un formulario HTML, aunque por otro lado en ocasiones puede ser lo que queramos.

Lo habitual sin embargo es que, en lugar de enviar parámetros, queramos enviar información arbitraria del tamaño que sea preciso, que es para lo que suele usarse POST (por ejemplo, un objeto JSON completo con información). Esto se puede conseguir modificando ligeramente el código anterior para incluir una cabecera que indique al servidor que lo que le llega son, precisamente, datos (línea 3 del siguiente fragmento alternativo):

http = getHttpRequest()
http.onreadystatechange = finCarga;
http.setRequestHeader('content-type', 'application/x-www-form-urlencoded');
http.open("POST", "http://www.miserv.com/misdatos.aspx", true)
http.send('Aquí ahora mando la información que quiera al servidor');

De esta manera podremos enviar información en formato JSON (luego lo veremos), los campos serializados de un formulario, o los contenidos de un archivo.

Problemas típicos de AJAX y cómo resolverlos

Ahora que ya conocemos los rudimentos de AJAX vamos a ver cuáles son los principales problemas que nos podemos encontrar al usar estas técnicas, y que en ocasiones pueden ser complicados de detectar.

OJO: Son los mismos problemas que nos encontraremos si utilizamos jQuery o alguna de las otras bibliotecas mencionadas, por lo que debemos ser conscientes de ellos.

Los más importantes son los siguientes:

Las bibliotecas como jQuery nos simplifican mucho el uso de AJAX ya que tienen en cuenta todos estos factores y algunos más.

1.- Llamadas fuera de dominio

Una vez que uno empieza a juguetear con las posibilidades de AJAX enseguida se nos ocurren ideas geniales para sacarle partido.

La más obvia, claro está, es la de utilizar las técnicas para acceder desde el cliente a ciertos Servicios Web de utilidad ajenos ubicados en Internet. Así, dado que los Servicios Web están basados en XML o JSON, es muy fácil procesar lo que devuelven con las técnicas descritas para, por ejemplo, realizar búsquedas en Amazon con su API, seguir una subasta en eBay, enviar “posts” a nuestro blog, consumir fuentes RSS, etc…

Todo esto es estupendo pero tiene un gravísimo problema: los navegadores, por cuestiones de seguridad, bloquean todas las peticiones realizadas mediante XmlHttpRequest a dominios que no sean el que aloja la página desde la que se está usando.

En realidad se trata de una restricción bastante lógica y que aparece en otras partes del navegador como ya hemos visto en otros módulos. Pero esto, claro está, supone una limitación importante para ciertos tipos de aplicaciones AJAX que podríamos desarrollar, como las de los ejemplos comentados.

La pregunta ahora es entonces: ¿Cómo solventamos esta situación?

En Internet Explorer basta con bajar el nivel de seguridad para que ya funcione correctamente, pero no es una buena solución (no le puedes pedir esto a tus usuarios). En otros navegadores (Firefox, Opera, Chrome y Safari) no hay forma de saltarse esta restricción. Existe una salvedad en Firefox que consiste en firmar digitalmente el JavaScript que usas, pero tampoco vale de mucho pues sólo funcionaría en este navegador.

La única forma de solucionarlo de manera independiente al navegador es, aunque sea obvio, hacer que no dependa de éste. Es decir, llevarnos el problema al servidor, donde estas restricciones no aplican. Para ello lo que debemos hacer es construir un servicio proxy que actúe de intermediario en nuestro servidor (al que sí podremos llamar con AJAX) y que sea éste el que se encargue de realizar la llamada a otros dominios devolviendo el resultado a nuestro JavaScript (directamente o preprocesándolo de algún modo).

Si construyes un servicio como este ¡ten mucho cuidado!. Normalmente este tipo de servicios -al igual que los que se encargan de leer archivos de disco de manera genérica y otros similares- son un verdadero peligro de seguridad si no los programamos bien. Si optas por esta solución lo mejor es que tomes varias precauciones de cara a la seguridad: tener muy acotados los servicios o URLs a los que se puede llamar desde el proxy. Lo mejor es identificarlos a cada uno con un número o código decidiendo a cuál se llama, pero nunca poniendo la URL directamente en la llamada desde JavaScript.

Otra solución, mucho más apropiada y de uso bastante extendido en la actualidad, requiere la connivencia con el servidor al que queremos acceder. Se trata de una técnica conocida como JSONP que utiliza otra forma diferente de acceder a los resultados. La veremos en un apartado específico un poco más adelante.

2.- Gestión de errores y llamadas que no vuelven

No podemos asumir que las llamadas que hagamos al servidor van a funcionar siempre. Puede haber un error en el código del servidor, es posible que haya cambiado la URL y que no exista la página que llamamos, que haya errores de permisos, etc… Lo que pase en el servidor está fuera de nuestro control. Ante eso hay que estar preparado. La forma de controlar estas situaciones es, como en cualquier componente de comunicaciones por HTTP, a través del código de estado que devuelva el servidor. Todo esto ya se había mostrado antes y se había tenido en cuenta en el código del manejador de fin de carga. Podríamos afinar más en el mensaje de error y devolver uno diferente según el código de estado.

Hay, sin embargo, una situación menos frecuente pero más peligrosa que se puede producir: que la llamada asíncrona al servidor no vuelva o no lo haga en un tiempo razonable, es decir que se produzca lo que se denomina un timeout. ¿Qué hacemos en ese caso?.

Lo que podemos hacer en estos casos es establecer un temporizador con el tiempo máximo que deseemos esperar, para que al cabo de ese tiempo la petición sea anulada directamente, sin esperar más que llegue la respuesta. Podemos verlo en este ejemplo:

var http = getHttpRequest()
http.onreadystatechange = finCarga;
http.open("GET", "misdatos.aspx", true)
var tmrAnular = setTimeout("AnularPeticion()", 20000); //20 segundos
http.send(null);
 
function AnularPeticion()
{
   http.abort();
}
 
function finCarga()
{
   if (http.readyState == 4) //4: completado
   {
      clearTimeOut(tmrAnular);
      if (http.status == 200) //200: OK
      {
         res = http.responseXML;
         Procesarespuesta();
      }
      else //Se produjo un error
      {
         alert("No se pudo recuperar la información: " + http.statusText);
      }
   }
}

Se ha modificado el código de llamada anterior para añadir la creación de un temporizador que se encarga de anular la petición al pasar un tiempo determinado (en este caso de 20 segundos, pero puede ajustarse a cualquier otro valor). Nótese también como en el evento de fin de carga eliminamos el temporizador (que ya no nos hace falta) cuando la petición termina de procesarse, en caso de que regrese.

3.- Contenidos no actualizados debido a cachés

Cuando se envía una petición HTTP es posible que, si la caché del lado servidor no está correctamente configurada, el navegador realice su propia caché local. Por lo tanto la próxima vez que realicemos una llamada a la misma URL, el navegador en lugar de hacerla sacará el mismo resultado anterior de esa caché local, y por lo tanto la llamada no llega al servidor jamás.

O puede que exista un proxy-caché por el medio (Telefónica/Movistar por ejemplo las ha utilizado tradicionalmente en sus servicios de acceso a Internet) que almacena peticiones anteriores y por lo tanto obtenemos únicamente una copia, sin realizar la llamada al servidor real. Eso muchas veces es lo que querremos para ahorrar procesamiento y será maravilloso, pero lo habitual en una aplicación que maneja datos es que suponga un problema, ya que evitará que obtengamos información actualizada.

A la hora de enviar datos por POST no hay problema porque no actúa nunca esa caché. El problema, si se da, está en las peticiones GET, por otro lado las más habituales.

Si el servidor tiene bien configurada la caché (es decir, indica cuándo caducan los contenidos o marcamos en el servidor que estos caduquen inmediatamente) no deberíamos experimentar fallos, salvando lo comentado respecto a los proxy-caché de algunos proveedores.

Si queremos asegurarnos de que la petición va a llegar a su destino podemos hacer fundamentalmente dos cosas:

http.setRequestHeader('If-Modified-Since', 'Wed, 1 Jan 1972 00:00:00 GMT');

Indicaremos siempre una fecha anterior a la actual como la del ejemplo y así siempre se pedirá la última versión al servidor.

http.open("GET", "misdatos.aspx?pasacache=" + new Date().getTime(), true);

Se le añade un parámetro que lleva como valor la fecha y hora en formato numérico (es decir, un número muy largo y que cambia varias veces cada milisegundo), por lo que es muy difícil que se den dos peticiones idénticas incluso a través de un proxy-caché.

Además ese parámetro extra de nombre inventado que nosotros le añadimos no debería afectar en absoluto a la llamada, puesto que no está siendo tenido en cuenta por la aplicación. Esta segunda técnica es la más fiable, aunque un poco más tediosa de implementar.

Devolución de información JSON

En el ejemplo anterior hemos hecho que la página del servidor devuelva ciertos valores, que en este caso tenían formato XML, pero que podrían tener perfectamente otra configuración distinta, como por ejemplo simples valores separados por comas. Si bien esto puede ser suficiente en los casos más sencillos, en otras ocasiones necesitaremos manejar estructuras de datos más complejas.

Dado que HTTP es un protocolo basado en texto, el recurso al que llamemos en el servidor debe devolver siempre texto (o sea, no puede ser una imagen o un archivo binario, que para transferirse se codifican de una forma especial -Base64- para convertirlos en texto).

Este texto devuelto puede tener cualquier formato: texto plano, XML, código JavaScript o incluso HTML. En este último caso podemos obtener desde el servidor un contenido HTML completo que se debe escribir en una zona de la página (por ejemplo dentro de un <div> o un <span>) y simplemente colocarlo ahí usando la propiedad innerHTML de los elementos del DOM.

Sin embargo, la mayor parte de las veces lo que tendremos que procesar es alguna estructura de datos.

Ya he mencionado que la 'X' de AJAX significa XML, pues era el formato de moda a finales de los 90 y principios de los 2000 y se usaba para todo. Este formato no es necesariamente el que se tiene que devolver. De hecho, hoy en día XML se usa muy raramente a la hora de representar los datos textuales devueltos desde el servidor en páginas AJAX.

El motivo de esto es principalmente que los datos representados con XML, si bien son ricos en estructura, hacen que el resultado devuelto ocupe mucho debido a las etiquetas de apertura y cierre de los diferentes nodos. Gracias al DOM es fácil procesar la información jerárquica representada mediante XML, sin embargo no deja de ser bastante tedioso también.

Como alternativa a XML surgió un nuevo formato programable llamado JSON (pronunciado “yeison”) que lo remplaza con mucha ventaja en la mayor parte de los casos. JSON es el acrónimo de JavaScript Object Notation, y ya hemos aprendido lo básico de utilizarlo en el módulo dedicado a la programación orientada a objetos.

Como su propio nombre indica permite representar objetos (en realidad estructuras complejas) en forma de código JavaScript que luego podemos evaluar. Su padre es Douglas Crockford.

JSON tiene varias ventajas sobre XML, a saber:

Consideremos el siguiente código XML que representa los datos de un cliente:

<cliente>
 <nombre>José Manuel</nombre>
 <apellidos>Alarcón Aguín</apellidos>
  <empresa>campusMVP</empresa>
 <telefono>986 165 802</telefono>
 <edad>40</edad>
</cliente>

Ahora consideremos la misma representación en JSON:

{
 "nombre" : "José Manuel",
 "apellidos" : "Alarcón Aguín",
 "empresa" : "campusMVP",
 "telefono" : "986 165 802",
 "edad" : 40
}

¿Te suena de algo?: Efectivamente. No es más que un objeto JavaScript normal y corriente. Crearlo es muy fácil pues es sintaxis JavaScript normal. En www.json.org es posible encontrar una explicación completa de esta notación, que ya hemos estudiado anteriormente.

Como se puede comprobar ocupa menos que el XML equivalente, es igual o incluso más fácil de leer que éste, y permite usar datos nativos y no sólo cadenas para representar los valores. En estructuras de datos más complejas se puede apreciar más todavía el ahorro de datos que implica, ya que podemos anidar objetos dentro del objeto principal.

De todos modos, lo más espectacular de JSON es lo fácil que resulta usarlo. Basta con escribir lo siguiente al recibirlo:

var cliente = eval(res);

Siendo res el nombre de la variable que contiene la cadena JSON devuelta por el servidor. Es decir, lo único que hacemos es procesar la expresión JSON. Al hacerlo obtenemos en la variable cliente un objeto cuyas propiedades son los datos que queremos manejar.

La función de JavaScript eval sirve para evaluar código JavaScript de manera dinámica. Si lo que evaluamos es un objeto, como en el caso del ejemplo, lo que devuelve eval es una referencia al mismo, ya en memoria. Pero puede procesar cualquier tipo de código JavaScript como si lo hubiésemos escrito en la página.

De este modo, ahora lo único que tenemos que hacer para leer cualquier dato devuelto en el nuevo objeto es escribir directamente expresiones como esta:

alert("El nombre de la empresa es " + cliente.empresa);

¡Más fácil imposible!. Nada de recorrer una jerarquía XML con el DOM o ir buscando nodo a nodo en el contenido. Se convierte en JavaScript puro y utilizable nada más llegar desde el servidor.

El problema del uso de JSON es recibir los datos de forma segura, de manera que no se nos pueda inyectar código malicioso en nuestra página, y por otro lado crear cadenas en formato JSON para enviarlas al servidor e intercambiar datos con éste desde el navegador.

Douglas Crockford creó en su día una biblioteca de JavaScript llamada json-js que sirve un doble propósito:

La biblioteca crea un objeto global llamado JSON que tiene estos dos métodos, a los que podemos llamar así:

var client = JSON.parse(res);
var sJson = JSON.stringify(miObjeto);

Aunque tienen algunos parámetros avanzados más.

La biblioteca es tan útil y ha sido tan exitosa que el ECMA decidió incluir el objeto JSON con estas dos funciones como parte integral del estándar en la versión 5 de la especificación del lenguaje (¡guau!, esto es tener éxito, ¿verdad?), por lo que todos los navegadores actuales disponen de esa funcionalidad incluida de serie.

Está soportada desde la versión 8 en Internet Explorer, la 3.6 de Firefox, la 19 de Chrome, la 5.1 de Safari y la 12 de Opera. Todos los navegadores móviles la soportan también.

De todos modos, si prevés que vas a tener que dar soporte a navegadores muy antiguos puedes descargarte desde el enlace anterior la biblioteca json2.js y utilizarla para, por el mero hecho de incluirla, dar soporte a navegadores que no incluyan el objeto JSON de serie.

JSONP: Accediendo a datos en otros dominios

La medida de seguridad y control más generalizada en los navegadores es la imposibilidad de acceso a recursos de otros dominios. Ya hemos presentado este problema en una lección anterior.

En cualquier navegador cada dominio es una zona aislada de las demás. Este aislamiento impide que, por defecto, se puedan transferir cookies entre dominios, que fallen los scripts que tratan de afectar a marcos con páginas que están en dominios diferentes y, por supuesto, tampoco permite hacer peticiones desde código JavaScript a servidores que están en dominios diferentes a la actual. Y aun así existen cantidad de vulnerabilidades (Cross Site Scripting, Cross Site Request Forgery, Cross Zone Scripting, etc…) relacionadas con robo de información y ataques distribuidos que están basadas en explotar código de JavaScript en aplicaciones mal construidas.

Al final, cómo esté escrito el código de una aplicación web desde el punto de vista de la seguridad es más importante que todos los cortafuegos y otras medidas “de protocolo” que puedas incluir.

Una de las implicaciones de este aislamiento entre dominios es que desde nuestro código JavaScript no podremos llamar a servicios Web que estén ubicados en dominios diferentes al nuestro (con XmlHttpRequest). Esto limita la capacidad de agregar información de diversas aplicaciones para explotarla directamente desde el navegador en nuestros desarrollos. Así que por defecto olvídate de llamar a código de Google, Twitter, Flicker, Facebook, YouTube, etc… y mostrar toda esa información agregada en tu página haciéndolo directamente desde JavaScript con los métodos convencionales.

Como hemos comentado antes, una solución habitual pasa por construir servicios web propios alojados en nuestro dominio, que actúen como intermediarios de otros servicios y nos devuelvan desde nuestro propio servidor la información que necesitemos, lo cual es tedioso y propenso a problemas de seguridad.

Otra solución alternativa se denomina JSONP (JSON with Padding). Fue propuesta en diciembre de 2005 por Bob Ippolito, creador de la biblioteca JavaScript Mochikit y actualmente trabajador de Facebook. Desde entonces ha sido adoptada por cada vez más aplicaciones Web 2.0 como forma de permitir las llamadas a servicios cross-domain.

Se trata de una manera de extender la sintaxis de JSON para que soporte llamadas desde otros dominios. Necesita la colaboración del servidor que expone los servicios para generar el código con una determinada sintaxis, y se aprovecha de que las etiquetas <script> permiten establecer el origen de los scripts (atributo src) en dominios diferentes al actual, no estando sujetas por tanto a las restricciones impuestas por el aislamiento de dominio.

Por ejemplo, una estructura de datos JSON común podría ser ésta:

{ Nombre : "José Manuel" }

Como hemos visto, la forma habitual de procesar estos datos por parte de un cliente JavaScript es evaluándolos para crear un objeto JavaScript asignado a una variable y luego hacer uso de ellos.

Sin embargo hay otra manera de recibir en el navegador ese código de script y que no implica usar el objeto XHR. Podemos escribir una etiqueta como esta o generarla dinámicamente usando JavaScript (que es lo más habitual):

<script type="text/javascript" src="http://www.otroservidor.com/usuarios/get/1/ "/>

En el atributo src indicamos el URL del servicio que nos va a proporcionar los datos. Estos se recibirán en el lado cliente sin problema a pesar de las restricciones entre dominios.

Sin embargo, como JSON define objetos para crear una estructura de datos pero no es directamente código ejecutable, lo que ocurrirá es que el objeto se evaluará pero se descartará inmediatamente, ya que no se está asignando a variable alguna. Se pierde.

Este problema se solucionaría si pudiésemos procesar o asignar de alguna manera los datos recibidos desde el servidor remoto. Es aquí donde entra en juego la colaboración de éste.

Si definimos en nuestro código JavaScript una función especializada en tratar la información devuelta y conseguimos que el servidor remoto devuelva, no sólo los datos “puros” en JSON, sino estos mismos datos envueltos en una llamada a dicha función, el problema estará solucionado.

Esta función puede ser una cuyo nombre esté previamente acordado, o bien una que se indique de manera dinámica y que el servidor utilizará como si se tratara de un método de retrollamada o callback:

<script type="text/javascript" src="http://www.otroservidor.com/usuarios/get/1/?f=ProcesaDatos/">

Por ejemplo, si tenemos en nuestro script una función llamada ProcesaDatos que se especializa en recibir objetos JSON y procesarlos, podemos indicar al servidor remoto que esta es la función en la que nos interesa recibir los datos. Éste devolvería la información envuelta en una llamada a esta función. De este modo el problema estaría solucionado.

En este ejemplo el servidor está de acuerdo en este tipo de procesamiento, así que le indicamos mediante un parámetro previamente acordado (formará parte de la API del servidor) que la función a llamar es ProcesaDatos, con lo cual en el script el servidor devolverá el objeto JSON envuelto en una llamada a dicho método, así:

ProcesaDatos( { Nombre : "José Manuel" } );

Lo que tenemos pues es una llamada automática a nuestra función con datos obtenidos desde otro servidor. ¡Justo la llamada entre dominios que estábamos buscando!. Es una forma de retrollamada o callback entre dominios usando JavaScript.

API: Interfaz de Programación de Aplicaciones. Un conjunto de funciones expuestas por un programa para poder ser automatizado desde otros programas. Habilitan la posibilidad de utilizar funcionalidad de unos programas desde otros.

Esta técnica de JSONP tiene muchas aplicaciones y está soportada también como método de llamadas a recursos remotos por parte de las bibliotecas de JavaScript más populares, como jQuery. Además en la actualidad muchas APIs de servicios on-line soportan ser llamados de esta manera.

Una biblioteca propia para JSONP

Para trabajar con JSONP lo único que hay que hacer es generar dinámicamente un nodo de tipo script en la página y agregarlo a algún lugar del documento (por regla general en la cabecera). Al hacerlo éste se procesa por parte de la página, lo que provoca que se descargue su código fuente y se ejecute. Podemos conseguirlo gracias a la magia del DOM, que nos permite añadir o quitar nodos a voluntad en el documento.

He escrito una pequeña utilidad que nos facilita el uso de JSONP en cualquier página con solo incluir el archivo .js correspondiente. No tiene dependencias con ninguna otra biblioteca y no interferirá tampoco con otras variables globales que puedas tener. Se llama JSONPHelper.js y lo tienes en los ejemplos descargables de este módulo.

El código es el siguiente:

var sendJsonpRequest = (function () {
    var queryScript;
 
    return function (url, callback, error, nomparam) {
        if (!nomparam)
            nomparam = "callback";
        var separador = "?";    //El separador del parámetro
 
        if (url.indexOf("?") >= 0)
            separador = "&";
 
        var head = document.getElementsByTagName("head")[0];
        if (queryScript) {
            head.removeChild(queryScript);
        }
        queryScript = document.createElement("script");
 
        queryScript.src = url + separador + nomparam + "=" + callback;
 
        if (error)
            queryScript.onerror = error;
 
        head.appendChild(queryScript);
    };
})();

El código puede parecer un poco lioso, pero vete acostumbrándote pues la mayor parte del código JavaScript actual se escribe de este modo.

Lo que se hace es guardar en una variable de nombre sendJsonpRequest el resultado de ejecutar una función anónima (fíjate en los paréntesis de la última línea, utilizados para forzar la ejecución de esta función anónima). Ésta lo único que hace es devolver otra función asociándola a la variable. De este modo, la variable pasa a convertirse en una función que podemos llamar como cualquier otra. La ventaja de hacerlo así, es que gracias al poder de las clausuras, es posible manejar variables internas privadas sin necesidad de convertirlas en variables globales externas a la función (en este caso la que se llama queryScript, definida al principio del código anterior).

Bien, entonces la verdadera funcionalidad está en esa función interna destacada en negrita que toma cuatro parámetros:

nomparam: parámetro opcional que nos sirve para indicar cómo se llama el parámetro que debemos pasar al URL en el que indicamos el nombre de la función de callback con la que se envolverá la información devuelta. Sería la f del URL de ejemplo anterior. Generalmente se llama callback, y este será el nombre usado por defecto en nuestro código, pero en algunos servicios puede cambiar.

El código es muy sencillo. Lo que hace primeramente es comprobar si se pasa o no el cuarto parámetro opcional. Si no es así le pone el nombre por defecto callback que es el más común.

A continuación, verifica si el URL que se le pasa ya contiene algún tipo de parámetro o por el contrario no tiene nada en la querystring. Eso lo hace simplemente verificando si hay alguna interrogación. En caso de haberla sustituye el “?” que añadiría normalmente para especificar el parámetro por un “&” para añadirlo al final.

Ahora ya viene el código que lanza la petición JSONP. Se localiza la cabecera de la página. Si ya le habíamos añadido algún script debido a una llamada anterior por JSONP lo eliminamos pues ya no nos hace falta (así ahorramos memoria). Se crea un elemento de tipo <script> usando el método createElement del DOM y se le ajusta su propiedad src para que apunte a la dirección de la API que nos interesa.

Si hay algún manejador de errores especificado se asigna al evento onerror del script, que salta si se produce cualquier error, por ejemplo que no cargue. Lo asignamos con el modelo del BOM porque estamos seguros de que seremos los únicos que lo van a usar, ya que creamos dinámicamente ese nodo y sólo está accesible desde nuestra función gracias a la clausura.

Finalmente añadimos el nodo de script a la cabecera de la página. Al hacerlo el navegador lo procesa para lo cual descarga el código indicado en src y lo ejecuta.

Lo que hemos conseguido es una ejecución dinámica de un script que está ubicado en otro servidor.

Con esta función a mano es muy fácil llamar a cualquier API basada en JSONP, como veremos a continuación con un ejemplo.

DEMO: Una base de datos de películas con JSONP

Ejemplo de uso de JSONP para crear un buscador de películas en una fuente externa (IMDb), con la API OMDb API.

IMPORTANTE: todo lo explicado en el vídeo a continuación es correcto y sigue funcionando perfectamente. La única salvedad es que, desde que se grabó, la API de OMDB ha decidido utilizar una clave de API para evitar abusos. Esta clave es gratuita (para 1.000 llamadas al día) y es muy fácil de obtener usando el enlace correspondiente del menú en la web. No obstante en la descarga de ejemplo he incluido una API mía para que puedas probarlo sin problemas. Todo lo demás es idéntico a lo explicado en el vídeo.

El HTML:

<!DOCTYPE HTML>
<html lang="en-US">
    <head>
	<meta charset="UTF-8">
	<title>Ejemplo del uso de la API de IMDB con JSONP</title>
 
	<link type="text/css" rel="stylesheet" href="IMDBInfo.css"/>
 
        <script type="text/javascript" src="EventHandlerHelper.js"></script>
        <script type="text/javascript" src="JSONPHelper.js"></script>
        <script type="text/javascript" src="IMDBInfo.js"></script>
    </head>
    <body>
	<div id="contenedor">
		<div id="cabecera"></div>
		<input type="text" id="titPelicula" value="Casablanca" />
		<input type="button" id="cmdGetInfo" value="Buscar películas" />
		<div id="Resultados">
			<div id="resBusqueda">
				&nbsp;
			</div>
			<div id="detalle">
				<div id="infoPeli">
					<h1 id="Title"></h1>
					<p id="Plot"></p>
					<h2>Ficha Técnica:</h2>
					<span class="encabezado">Tipo: </span><span id="Type"></span><br/>
					<span class="encabezado">Fecha de lanzamiento: </span><span id="Released"></span><br/>
					<span class="encabezado">Duración: </span><span id="RunTime"></span><br/>
					<span class="encabezado">Director: </span><span id="Director"></span><br/>
					<span class="encabezado">Reparto: </span><span id="Actors"></span><br/>
					<span class="encabezado">Pais: </span><span id="Country"></span><br/>
					<span class="encabezado">Premios: </span><span id="Awards"></span><br/>
				</div>
				<div id="caratula"><img id="imgCaratula"/></div>
			</div>
		</div>
	</div>
    </body>
</html>

Scripts usados:

El contenido de IMDBInfo.js:

var IMDB_API_URL = "http://www.omdbapi.com/";
var IMDB_API_KEY = "be50bfe7";
 
function hacerBusqueda() {
    var titPelicula = document.getElementById("titPelicula").value;
    if (titPelicula.trim() == "") {
        alert("Introduzca el título de una película para buscarla en la Internet Movie DataBase");
        document.getElementById("titPelicula").focus();
    }
 
    sendJsonpRequest(IMDB_API_URL + "?apikey=" + IMDB_API_KEY + "&s=" + titPelicula, "mostrarResultadosBusquedaCallback", error404);
}
 
function titPelicula_OnEnter(event) {
    event = EventHandlerHelper.fixEvent(event);
    if (event.which == 13)
        hacerBusqueda();
}
 
//Muestra resultados de búsqueda (función callback de JSONP)
function mostrarResultadosBusquedaCallback(resultados) {
    var resHTML = "";
 
    if (!resultados.Search || resultados.Search.length == 0){
        resHTML = "<span>¡¡No hay películas con este título!!</span>";
    }
    else {
        for(var i = 0; i<resultados.Search.length; i++) 
        {
            var res = resultados.Search[i];
            resHTML += "<p data-imdbid='" + res.imdbID + "' onclick='mostrarInfoPeli(this);'>" + res.Title + "(" + res.Year + ") - " + res.Type + "</p>";
        }
    }
    document.getElementById("resBusqueda").innerHTML = resHTML;
    ocultarDetalles();
}
 
//Si no se encuentra la API
function error404()
{
    document.getElementById("resBusqueda").innerHTML = "<span>¡¡Error al conectar con la API!!</span>";
    ocultarDetalles();
}
 
//Muestra info de la película a partir de un identificador
//Se le pasa el elemento sobre el que se ha pulsado
function mostrarInfoPeli(elto) {
    var idPelicula = elto.attributes["data-imdbid"].value;
    //Desmarco las que hubiese seleccionadas
    var seleccionados = document.getElementsByClassName("seleccionado");
    for(var i = 0; i< seleccionados.length; i++) {
        seleccionados[i].className = "";
    }
    //La marco como seleccionada
    elto.className = "seleccionado";
    //Pido la info sobre la película
    sendJsonpRequest(IMDB_API_URL + "?apikey=" + IMDB_API_KEY + "&i=" + idPelicula, "mostrarInfoPeliculaCallback", error404);
}
 
//Muetra información de una película (función callback de JSONP)
function mostrarInfoPeliculaCallback(peli){
    if (peli != null && !peli.Error)
    {
        //Asignamos a mano la imagen
        if (peli.Poster == "N/A")
            document.getElementById("imgCaratula").style.visibility= "hidden";
        else {
            document.getElementById("imgCaratula").src = peli.Poster;
            document.getElementById("imgCaratula").style.visibility= "visible";
        }
        //Ahora asignamos el resto de los campos
        enlazarCamposConDOM(peli);
        mostrarDetalles();
    }
}
 
//Se encarga de localizar campos en la página con el mismo nombre que campos de un objeto que se le pasa, e introduce su valor como contenido en cada uno de ellos
function enlazarCamposConDOM(obj) {
    //Si no se pasa un elemento concreto del DOM a partir del cual buscar, se usa document y se busca en toda la página
    //Se recorren las propiedades del objeto
    for (prop in obj) {
        //Verificamos si existe un elemento con el mismo id que la propiedad
        var e = document.getElementById(prop);
        if (e != null) {
                e.textContent = obj[prop];  //Asignamos el valor como contenido de texto
        }
    }
 
}
 
//Mostrar detalles película
function mostrarDetalles(){
    document.getElementById("detalle").style.display= "block";
}
 
//Ocultar detalles película
function ocultarDetalles(){
    document.getElementById("detalle").style.display= "none";
}
 
function initialize() {
    EventHandlerHelper.addEventListener(document.getElementById("cmdGetInfo"), "click", hacerBusqueda);
    EventHandlerHelper.addEventListener(document.getElementById("titPelicula"), "keyup", titPelicula_OnEnter);
}
 
window.onload = initialize;

Visor online para JSON: https://jsonviewer.stack.hu/. Pegamos un objeto JSON y podremos visualizar en formato árbol o formatearlo.

Control de acceso entre dominios: CORS

La funcionalidad de JSONP que acabamos de estudiar es muy interesante para crear mashups y manejar información de otros dominios de manera directa desde el navegador. Sin embargo, hay que tener cuidado cuando se usa con información privada importante ya que no está exenta de problemas de seguridad. Por algo los que diseñan los navegadores se empeñan en no permitir el acceso entre dominios.

Mashups: Aplicaciones que centralizan en un solo punto elementos de otras aplicaciones o páginas web. Por ejemplo, una página que integre en una sola interfaz todas las notificaciones de tus redes sociales.

No voy a entrar en detalles aquí sobre técnicas de phising que usan JavaScript para obtener ilícitamente información de otros dominios mediante JSONP, pero baste decir que Twitter o el mismísimo GMail fueron craqueados hace años usando técnicas de Cross Site Request Forgery (también llamado XSRF) basadas en llamadas JSONP.

El protegerse de esas técnicas de hacking depende del servicio que implementa la API JSONP. Sólo deberíamos usarlo en nuestras páginas si confiamos en estos sitios. Utilizar APIs de servicios poco recomendables podría resultar en problemas de seguridad para nuestros usuarios.

Además JSONP tiene bastantes limitaciones:

Así que, realmente, la mejor manera de crear aplicaciones AJAX sigue siendo usar el objeto XmlHttpRequest (XHR). Y con las modernas aplicaciones web cada vez más extendidas y ávidas de funcionalidad, el poder acceder a otros dominios de manera lícita pero a la vez controlada es una necesidad acuciante.

Conscientes de ello el W3C trabajó en un estándar que modifica el modo de funcionamiento tradicional del objeto XmlHttpRequest para dotarlo de capacidades de acceso a otros dominios. También define las convenciones necesarias para que los servidores que ofrecen servicios puedan regular el acceso de los navegadores a los mismos. La especificación se denomina Cross-Origin Resource Sharing (compartición de recursos entre diferentes orígenes) o más concisamente con el acrónimo CORS.

Los principales navegadores lo han implementado hace bastante tiempo (Internet Explorer desde la versión 8, Opera desde la 12). Si te interesa puedes leer el estándar completo aquí: http://www.w3.org/TR/cors/.

Nota: Muchos programadores cuando oyen hablar de CORS piensan en un protocolo de seguridad para proteger el acceso a los recursos, de manera similar a tener una clave o usar un mecanismo de cifrado o algo similar. Se trata de un error de concepto. CORS no pretende proteger el acceso a las aplicaciones de servidor. No es un método de seguridad para servicios en Internet. Lo que busca es proteger a los usuarios frente a ataques de phishing y XSRF para que no sean víctimas de un pirata de forma inadvertida, que es algo muy diferente.

Funcionamiento básico de CORS

CORS define un protocolo entre los servidores Web y los navegadores para que colaboren a la hora de delimitar políticas de seguridad de acceso entre dominios.

Así, en su forma más básica, CORS define que cuando se haga una llamada con XHR a un dominio diferente del actual, dicha llamada debe incluir automáticamente información sobre quién realiza la petición. Así ésta incluirá automáticamente una cabecera llamada “Origin” que le indicará al servidor de destino el protocolo, dominio y puerto desde el que se está originando la petición.

Nota: Esta cabecera difiere de la cabecera “Referer” tradicional en que no se indica ningún tipo de ruta, por lo que las preocupaciones de privacidad de los posibles usuarios son menores.

Con esta cabecera el servidor decide si permite o no el acceso al recurso, devolviendo en su respuesta una cabecera de tipo “Access-Control-Allow-Origin” en la que indica al navegador qué dominios tienen permiso para realizar las peticiones.

Por ejemplo, puede devolver:

Access-Control-Allow-Origin: http://www.tuservidor.com

Al recibir esta cabecera el navegador sabe si debe permitir o no que se realice la llamada. De esta forma se protege a los usuarios, ya que si alguien intenta un ataque XSRF contra un servicio como GMail, al realizar la petición desde otro dominio distinto al esperado (mail.google.com), el navegador la bloqueará y su código malicioso no servirá de nada.

Por defecto, si el servidor no incluye esta cabecera el navegador bloquea la llamada, con lo que el comportamiento es exactamente el mismo que el tradicional.

En muchos casos, si el servicio quiere que pueda consumirse desde cualquier dominio, puede devolver esto:

Access-Control-Allow-Origin: *

Esto significa que admite peticiones desde cualquier dominio externo. Ideal para servicios que no juegan con datos privados y quieren abrirse a cuantos más usuarios externos mejor.

La especificación CORS define todavía más cabeceras para cuestiones algo más especializadas, como por ejemplo para cachear el resultado de una valoración de seguridad y cuánto tiempo permanece guardada o las “preflight requests” para lanzar peticiones previas con el método HTTP OPTIONS y obtener las condiciones de seguridad de las llamadas desde ese origen para toda la sesión.

Lo mejor de CORS es que nosotros, como programadores JavaScript no tenemos que hacer nada especial para soportar estas llamadas entre dominios. Nos limitamos a utilizar el objeto XHR como siempre, de la manera explicada en este tema. La única diferencia es que tenemos que incluir el URL completo, con el nombre de otro dominio.

A la hora de utilizar un servicio externo desde uno de tus desarrollos JavaScript, intenta averiguar si el servicio implementa cabeceras CORS de tipo “*”. Te simplificará enormemente el desarrollo pues podrás sacar partido al objeto XHR como si fuera un servicio albergado en tu propio dominio.

Recursos