Banner

Ultima revisión 09/04/2013

AJAX Cross-Domain: jQuery + PHP

Hoy me he encontrado con la necesidad de hacer una llamada a una aplicación que estoy realizando en IslaVisual y que trabaja en modo HTML y JSON. Todo fue bien mientras trabajaba con llamadas locales en el entorno de desarrollo de localhost pero, al publicarlo al servidor de PreProducción y tener que llamar a otro dominio para hacer una consulta, me encontre con el problema de que jQuery daba un error Status = 0.

Estuve dando vueltas sin parar, por el maravilloso mundo de Internet, durante horas hasta que mi compañero me comentó que JavaScript y los navegadores bloquean las llamadas entre dominios por temas de seguridad. A este problema, ya le dió solución Bob Ippolito en 2005 que fue quién propuso el uso de una versión modificada de JSON denominada JSONP. A partir de entonces, muchos sitios y servicios han utilizado esta técnica para que se puedan consumir sus APIs desde una aplicación del lado del cliente.

Yo, como la gran mayoría de los desarrolladores, he utilizado esta técnica desde este lado (el lado del cliente) y, para entendernos, dicho con otras palabras, me daban una URL y, como buen usuario, la embebía en una función getJSON, ajax, post o get y el resultado, mas tarde, lo parseaba o lo incrustaba en otro elemento HTML.

Ahora la cosa ha cambiado. Ahora estoy en el lado del Servidor y, por ello, me ha tocado cambiar el punto de vista.

Cosas a tener en cuenta en la Aplicación remota (en PHP)

Según la documentación de JSONP, se debe permitir que el cliente añada algo de contenido al JSON devuelto, siendo este englobado en paréntesis, básicamente para que lo que se devuelva sea la “ejecución de un función”, que será creada (de la forma que sea) en el cliente, para su ejecución al finalizar la llamada AJAX.

Por lo tanto, la idea básica es, teniendo un servicio web que devuelva un JSON válido, permitir dos nuevos parámetros: uno para representar el formato que diferenciará las llamadas remotas de las realizadas desde nuestra aplicación (si la hubiera) y otro, el nombre de la función a ejecutar en el cliente. Los nombres de estos parámetros en jQuery son dataType (con valor jsonp) y jsonpcallback (que puede tomar cualquier valor) aunque, este último, no es obligatorio.

Entonces me puse a pensar y me di cuenta de que todas las llamadas a APIs en jQuery usan un parámetro llamado callback que, normalmente, está establecido a ? o a un nombre de función. Ahora todo empezaba a tener sentido porque a través de este parámetro se podía dar uso a la funcionalidad que JSONP mencionaba.

Con el siguiente ejemplo, espero que cojáis una primera idea.

$format = $_REQUEST['format'];
if($format == "json" || $format == "jsonp"){
    if(isset($_REQUEST['callback'])) {
        echo $_REQUEST['callback']."(" . json_encode($json) . ")";
    } elseif(isset($_REQUEST['jsoncallback'])) {
        echo $_REQUEST['jsoncallback']."(" . json_encode($json) . ")";
    } elseif(isset($_REQUEST['jsonpcallback'])) {
        echo $_REQUEST['jsonpcallback']."(" . json_encode($json) . ")";
    } else {
        echo "callbackFunction(" . json_encode($json) . ")";
    }
}

De esta manera, si no se indica una función, indicamos una por defecto, otra opción es no permitirlo y devolver error.

La variable $json sería la representación de lo que se devolvía desde un principio en el servicio web, es decir, una cdena formateada en JSON.

Cosas a tener en cuenta la Aplicación local (en jQuery)

Esta parte es la más conocida porque, estaremos de acuerdo, prácticamente todos hemos hecho alguna vez una llamada a la API de Twitter, Facebook o Google+.

Lo que vamos a hacer es incluir en el dataType el valor jsonp, en vez de json, para indicar que estamos realizando una petición cross-domain y que esperamos este tipo de contenido. Cuando se realice la petición JSONP, jQuery añadira un identificador único a cada petición que irá en forma de atributo callback. Si inspeccionáis una llamada jQuery desde, por ejemplo, el FireBug, el valor devuelto por la variable callback será parecido a jQuery17207112888727362633_1334060373256.

function callback(data){
    alert(data);
}

$.ajax({
    url: 'http://api.islavisual.com/news',
    dataType: "jsonp",
    crossDomain:true,
    data:{
        tags: "desarrollo web",
        format: "jsonp"
    },
    jsonpCallback: callback
});

$.getJSON('http://api.islavisual.com/news?jsonpcallback=?', {
    tags: "desarrollo web",
    format: "jsonp"
}).done(function( data ) {
    alert('done');
});

Desde la versión 1.5 de jQuery se puede forzar una solicitud cross-domain (como JSONP) en el mismo dominio. Para ello sólo hay que establecer el parámetro crossDomain:true. Con esto ya se permitiría la llamada a otro dominio. Si esta opción se establece podemos, además, omitir el parámetro jsonpcallback=?, callback=? o su variación correspondiente.

Esto son dos versiones de lo mismo. El ejemplo traería las noticias que contengan el tag desarrollo web. En el primer código de ejemplo (con jsonpcallback=callback), el resultado se enviará al método callback. En el segundo (con jsonpcallback=?) pasaría por el evento done.

tags y format son los parámetros que se enviarán como datos.

En la llamada $.ajax podemos cambiar el parámetro dataType que indica que la llamada devolverá un JSON. Entre otros, puede tomar los valores de text, html y json.

Espero que os sea de ayuda como a mí. Si necesitas más información Contacta con nosotros y te informaremos más detalladamente sobre este o cualquier otro tema.