Sopa de Siglas: AJAX, JSON, JSONP y CORS

Al jugar con PhoneGap/Cordova he acabado por tocar un punto que puede ser un tanto escabroso en según que casos: el acceso a servidores externos. Hacer esto desde Javascript supone enfrentarse a un mar de siglas que pueden llegar a ser confusas.

AJAX y JSON

AJAX son las siglas de Asynchronous Javascript And Xml (Javascript Asíncrono y Xml) y es una forma de poder ejecutar código javascript de manera asíncrona para obtener información.

Empezó a cobrar importancia al extenderse XMLHttpRequest, un interface creado por Microsoft que permite realizar peticiones HTTP/HTTPS mediante Javascript e intercambiar información en cualquier formato de texto plano (HTML, XML o JSON).

La X de AJAX se refiere a XML porque al principio el formato más usado para intercambiar datos era XML, pero hoy en día el formato más frecuente seguramente sea JSON, JavaScript Object Notation (Notación de objetos en JavaScript).

JSON es un formato mucho más ligero que XML y, además, tiene la ventaja de ser “nativo” para Javascript, el lenguaje por excelencia para usar XMLHttpRequest desde una página web.

JSONP

Por motivos de seguridad, usando XMLHttpRequest sólo pueden realizarse peticiones al mismo dominio desde el que se ha cargado la página que origina la petición. Es decir, si cargamos una página desde www.domain.com, sólo se podrán hacer peticiones a URLs de la forma www.domain.com/lo-que-sea.

La idea es que si navegas a una página web con código “malicioso” y tienes una sesión abierta a, por ejemplo, la página de tu banco, esa página web maliciosa no pueda lanzar una petición http para ordenar una transferencia.

El problema es que a veces este tipo de peticiones son legítimas y necesarias. Por ejemplo, podemos tener una página que está agregando información de varias fuentes y, para ello, tenemos que lanzar peticiones a los dominios de esas fuentes.

Para solucionar este problema surge JSONP, JSON with Padding (JSON con almohadillas/relleno). JSONP se basa en inyectar un script en la página actual usando al etiqueta <script src=...>, que sí permite acceder a recursos situados en otros dominios.

Como inyectar un fragmento de JSON tal cual es poco útil, se emplea el padding, que no es más que una función definida por el cliente y que el servidor usa para “rodear” el fragmento de JSON devuelto.

Por ejemplo, podríamos tener un API que devuelve el tiempo en Madrid usando JSONP y que funcionaría así:

// Una llamada a la url http://weather.com/?location=Madrid
// Devuelve este fragmento JSON
{
    "location": "Madrid",
    "temperature": "20"
}

Para invocarlo con JSONP, se añadiría un nuevo parámetro con la función callback que queremos que se ejecute con la respuesta:

// La llamada a http//weather.com/?location=Madrid&callback=processResult
// Devolvería este script
processResult({"location": "Madrid", "temperature": "20"});

De esta forma, cuando se carga en la página el script con el resultado, está incluyendo el código necesario para invocar la función que queremos usar para procesarlo. Las librerías modernas de Javascript como jQuery son capaces de tratar esto de forma bastante automática.

JSONP tiene varios inconvenientes, como que sólo admite peticiones GET (olvídate de POST y similares) y no permite controlar el proceso de la petición, ya que se delega por completo a la forma en que procese el navegador las etiquetas <script src=...>.

CORS

Dadas las limitaciones de JSONP, y que no deja de ser un hack, recientemente aparece un protocolo nuevo, CORS, Cross-Origin Resource Sharing (Compartición de recursos de distintos orígenes).

CORS permite realizar peticiones a otros dominios siempre y cuando el dominio de destino esté de acuerdo en recibir peticiones del dominio de origen. Es una tecnología implementada a nivel de navegador, que intercambia ciertas cabeceras HTTP con el servidor de destino para saber si debe permitir o no el intercambio de datos.

En principio el código Javascript que realiza la petición a través de XMLHttpRequest no tiene que saber nada de CORS, pero el navegador donde se ejecuta y el servidor al que se conecta sí tienen que haber implementado el protocolo. Si queréis ver un ejemplo de cómo se implementa CORS en el servidor, aquí tenéis cómo implementar CORS en ASP.NET Web API.

En su forma más básica, cuando un servidor que implementa CORS recibe una petición añade a la respuesta una cabecera HTTP con el siguiente aspecto:

Access-Control-Allow-Origin: http://dominio-permitido.com

La cabecera incluye el/los dominios desde los que se permite acceder a este recurso y es responsabilidad del navegador decidir si la página que hizo la petición ha sido cargada desde uno de esos dominio o no, y devolverle los datos a la página o no.

¿Cómo afecta todo esto a PhoneGap/Cordova?

Todo este rollo viene a cuento porque las aplicaciones que empaquetamos con PhoneGap/Cordova son cargadas desde el sistema local de archivos, con lo que cualquier petición que realicemos a internet se convierte en una petición cross-domain.

Ahora que ya conocemos las bases de todo esto (y ya sabéis que conocer la teoría es algo que me importa), estamos en condiciones de ver cómo podemos hacer peticiones cross-domain en nuestra aplicación PhoneGap/Cordova, aunque eso lo dejaremos para el próximo post.

PD: Para los puristas, sí, el título está mal. Algunos no son siglas, son acrónimos :-)

6 comentarios en “Sopa de Siglas: AJAX, JSON, JSONP y CORS

  1. Pingback: Acceso a recursos externos con PhoneGap/Cordova « Koalite's blog

  2. Muy bueno, gracias. El primer post que encuentro como la gente, bien explicado y detallado. Gracias! Ahora me quedo una noción mucho mejor de lo que es cross-domain!
    Para ASP.NET MVC tengo un problemita, no me funciona agregando las respectivas etiquetas en el web.config. Alguna idea de porque no funciona?

    Saludos Gracias!

  3. Pues no tengo ni idea. ¿Has probado con fiddler a ver si las cabeceras se envían correctamente de un sitio a otro?

  4. Pingback: Los 4 principios de REST | Absa

  5. Pingback: Web Api 2 Consideraciones para el Desarrollo – Viejo Programmer

Comentarios cerrados.