jQuery: ajax desde diferentes dominios (cross domain ajax)

Las llamadas ajax a diferentes dominios desde el que carga el script es algo "prohibido" por todos los navegadores modernos, algo achacado a motivos de seguridad pero que sin duda puede no ser práctico en determinados casos. La llamada ajax es prohibida incluso entre subdominios diferentes. No obstante, hay diferentes modos de saltarse esta restricción. Uno de ellos es usar un método que, aunque en esencia es un ajax, los datos cargados son en sí un script, permitiéndose en el navegador.

El método que vamos a ver aquí es realizar la llamada ajax con jQuery usando jsonp como leguaje de intercambio de datos.

¿Qué es jsonp?

jsonp es es la versión padding de json. Esto es, json con padding, un complemento de uso de json que permite la obtención de datos desde diferente dominios ya que los datos devueltos son en formato json encapsulados en una función javascript ...... y los scripts pueden ser cargados desde diferentes dominios!!!!!!. Técnicamente jsonp no es ajax en absoluto ya que no usa XMLHttpRequest sino que se realiza insertando dinámicamente un script en el DOM de la página.

La respuesta en formato json es obtenida en una forma similar a esta:

{'uid': '68', 'username': 'paquito', 'status': 'disconected'} 

Mientras que los datos jsonp vienen en una función javascript:

callback({'uid': '68', 'username': 'paquito', 'status': 'disconected'}); 

La función javascript devuelta (callback) debe ser especificada en el lado del servidor y conocida para cofigurar nuestra llamada ajax con jquery y jsonp correctamente. Esto lo veremos más adelante.

¿Cómo usar jquery con jsonp?

Se puede usar jQuery para leer y trabajar con datos jsonp usando cualquiera de las funciones de jQuery .getJSON() o .ajax(), ambas soportan de forma nativa jsonp. También se puede implementar con las funciones .post() y .getScript().

Veamos como usar .ajax() y .getJSON().

La función .getJSON() es una forma más corta de escribir una función .ajax() que va usar datos tipo json:

.getJSON(url,[datos enviados],function(datos devueltos)); 

Los [datos enviados] son opcionales y han de ir en formato json.

Para especificar que los datos a usar son jsonp simplemente se debe poner al final de la url algo como &callback=?.  callback será la función que contiene los datos recibidos en formato json. Esta función debe ser conocida, por ejemplo flickr usa "jsoncallback" por lo que al final de la url tendremos que poner &jsoncallback=?.

Para usar la función .ajax() con jsonp simplemente se especifica que los tipos de datos son jsonp. Al hacer esto jQuery pone automáticamente &callback=? al final de la url solicitada. Si es necesario escpecificar otro callback debes ponerlo especificamente:

.ajax({ 
  //....aquí usarías .ajax() de forma normal 
  dataType: 'jsonp', 
  callback: 'callback', //si no pones ninguno será &callback=? por defecto 
  //....sigues usando .ajax() de forma normal 
}): 

jQuery recibirá los datos jsonp y los trata de forma igual a como lo hace con json. Veamos el ejemplo que pusimos en ajax con jquery, php y json pero ahora usando jsonp.

En el ejemplo mencionado teníamos tres archivos, un html desde donde mostramos los datos, un js donde ponemos nuestro código ajax de jquery y un archivo php donde procesamos la respuesta enviada por el servidor en función de los datos recibidos.

El html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="es-es" lang="es-es" >
 <head>
  <script type="text/javascript" src="/js/jquery.js"></script>
  <script type="text/javascript" src="/js/ajax.js"></script>
 </head>
 <body>
  <a href="#" id="myid">Detalles casa 2</a>
  <div class="info"></div>
 </body>
</html>

El código ajax con jquery

El siguiente código sería el que ponemos en el archivo ajax.js cargado en nuestro html:

function restults(data) {
       $("div.info").show();
       $("div.info").append("ID: "+data.id);
       $("div.info").append("M2: "+data.m2+"");
       $("div.info").append("Habitaciones: "+data.hab);
       $("div.info").append("Lacalización: "+data.direccion);
     }
  $(document).ready(function(){
     $("#myid").click(function(){
       $.ajax({
         data: "id=2",
         type: "GET",
         dataType: "jsonp",
         url: "http://www.miweb.com/casas.php",
         success: function(data){ 
           restults(data); 
         }
       });
    });
  }); 

Nota que no he especificado un callback, por tanto jQuery pondrá al final de la url &callback=? por defecto. En el archivo php verás por qué es necesario conocer el callback para que nuestro ajax tenga éxito.

Nota también como la sintaxis en jQuery para trabajar con los datos recibidos no cambia.

El archivo php

Este archivo php estaría en otro dominio distinto al dominio donde estaría el html y javascript anterior. Este sería un ejemplo de como lo podríamos construir si este segundo dominio también es nuestro. En caso de que queramos hacer un crossdomain ajax a un dominio que no es nuestro, el dueño de este domino tendrá que proveer de un API con los datos necesarios para trabajar, como mínimo el callback que debemos utilizar, las variables que podemos enviar y los datos que podremos recibir. Por ejemplo flickr.com lo incorpora en su API (vea Formato de respuesta JSON en el API de flickr).

<?php 
(int) $id = $_GET['id'];
if($id > 0) {
   process($id);
 }

 function process($id) {
   $db = mysql_connect('localhost', 'mysql_user', 'mysql_password'); 
   if (!$db) { 
     die('Could not connect: ' . mysql_error());
   } 
   if (!mysql_select_db('nombre_base_de_datos')) { 
     die('Could not select database: ' . mysql_error());
   }    
   $result = mysql_query("SELECT id,m2,hab,direccion FROM casas WHERE id=".$id); 
   if (!$result) { 
     die('Could not query:' . mysql_error()); 
   }

   $row = mysql_fetch_object($result);
   $jsondata['id'] = $row->id;
   $jsondata['m2'] = $row->m2;
   $jsondata['hab'] = $row->hab;
   $jsondata['direccion'] = $row->direccion;

   $json = json_encode($jsondata); 
  if($_GET['callback']) { 
    echo $_GET['callback']."(".$json.")"; 
  } else { 
    echo $json; 
} 
mysql_close($link);
 } 
?>

Puedes ver que se verifica la existencia del callback a través de $_GET['callback'], podría ser por ejemplo $_GET['jsoncallback'] como en el caso del API de flickr y en este caso tendremos que especificar este callback en la función .ajax() tal y como explicamos anteriormente. Si no existe el callback el script php devuelve los datos en formato json normal.



Comentarios (15)

Community Builder Avatar
cybnet
(07.05.2012 (09:49:39))
El callback se explica en el post Sí No Citando "jose francisco" :
mira lei otro foro y encontre que tenia que definir un atributo jasonp en la peticion, el cual llevaria el nombre de mi callback function, entonces del lado del servidor obtienes por get el callback, y si esta definido, pues ya puedes realizar operaciones y mandar una respuesta al cliente.
Todo eso del callback se explica en el post, si lo hubieses leído antes de preguntar te hubiéses ahorrado mucho tiempo, ¿no crees? lol
Community Builder Avatar
jose francisco
(07.05.2012 (04:52:43))
Correccion Sí No perdon, me equivoque en el codigo anterior, el ultimo echo seria algo asi:
Código :

echo $jsonp_callback . "(". json_encode($msg) .");";


gracias
Community Builder Avatar
jose francisco
(07.05.2012 (04:49:49))
definir el atributo jasonp Sí No Citando "jose francisco" :
Citando "cybnet" :
Saludos Jose Francisco,
Así a primera vista parece que debería funcionar si solventas varios fallos de sintaxis tanto en el javascript como en el php, por ejemplo nota el espacio que tienes en la variable $num_tarjeta:Código :
$respuesta = $obj2->preautorizarPago($num_tarje ta, $mes, $year, $codigo);


fijate que ya cheque la sintaxis y esta bien, ahora donde me marca el error es en "jasonp",
me dice el firebug que no esta definido unsure


mira lei otro foro y encontre que tenia que definir un atributo jasonp en la peticion, el cual llevaria el nombre de mi callback function, entonces del lado del servidor obtienes por get el callback, y si esta definido, pues ya puedes realizar operaciones y mandar una respuesta al cliente.
Hice algo como esto:

Código :

if(isset($_GET['jsonp_callback'])){
$jsonp_callback = $_GET['jsonp_callback'];
$msg = array("mensaje" => "Se va a cobrar esta cantidad: $" . $_GET['monto'] );
echo $msg;
}
Community Builder Avatar
jose francisco
(06.05.2012 (20:30:09))
Sí No Citando "cybnet" :
Saludos Jose Francisco,
Así a primera vista parece que debería funcionar si solventas varios fallos de sintaxis tanto en el javascript como en el php, por ejemplo nota el espacio que tienes en la variable $num_tarjeta:Código :
$respuesta = $obj2->preautorizarPago($num_tarje ta, $mes, $year, $codigo);


fijate que ya cheque la sintaxis y esta bien, ahora donde me marca el error es en "jasonp",
me dice el firebug que no esta definido unsure
Community Builder Avatar
cybnet
(06.05.2012 (10:38:51))
Varios fallos de sintaxis Sí No Saludos Jose Francisco,
Así a primera vista parece que debería funcionar si solventas varios fallos de sintaxis tanto en el javascript como en el php, por ejemplo nota el espacio que tienes en la variable $num_tarjeta:Código :
$respuesta = $obj2->preautorizarPago($num_tarje ta, $mes, $year, $codigo);
Community Builder Avatar
jose francisco
(06.05.2012 (09:20:32))
Sí No hola oye estoy intentando hacer una peticion a un dominio, pero no me funciona, lo que intento hacer es mandar datos de un formulario a un servidor y despues el servidor me manda una respuesta.

esto es lo que tengo

Código :

var dataString = 'nombre='+ name + '&apellido=' + apellido + '&mes=' + mes + '&year=' + year + '&numTarjeta=' + tarjeta + '&codigoSeguridad=' + codigo + '&monto=' + monto;

$.ajax({
type: "GET",
url: "http://pagos.sduninter.tk/servidor_pagos.php",
crossDomain : true,
data: dataString,
dataType: jsonp,
success: function(data) {
alert("wazzaa");
},
error: function(){
alert("No jala");
}
});


y del lado del servidor tengo esto:

Código :

if(isset($GET['numTarjeta'])) {

$num_tarjeta = $_GET['numTarjeta'];
$mes = $_GET['mes'];
$year = $_GET['year'];
$codigo = $_GET['codigo'];

$obj2 = new servidor_pagos();

$respuesta = $obj2->preautorizarPago($num_tarje ta, $mes, $year, $codigo);

}

class servidor_pagos{
public function preautorizarPago($tarjeta, $mes, $year, $codigo) {
$msg['mensaje'] = "A webo hay un chingo de varo !";
$json = json_encode($msg); if($_GET['callback'])
echo $_GET['callback']."(".$json.")"; else
echo $json;

}
}
Community Builder Avatar
cybnet
(26.04.2012 (09:51:05))
Claro que no funciona Sí No Citando "lady" :
al parecer no funciona el enlace con ajax solo le sale el link del archivo html y nada mas
Es evidente que tal y como está el código no va a funcionar. Primero tendrás que disponer de dos dominios, en uno cargar el html y el javascript y en el otro poner el PHP. El código PHP mostrado de ejemplo se supone que realizará una llamada a una base de datos por lo que tendrás que modificar ese código para que no consulte a ninguna base de datos o poner en el lugar correspodiente del código los datos de acceso a la base de datos que hayas creado con las tablas necesarias para el ejemplo. Una vez tengas este PHP listo en el otro domino tendrás que cambiar la url de solicitud del código javascript de "url: "http://www.miweb.com/casas.php"," a la que sea correcta en tu caso.
Community Builder Avatar
lady
(26.04.2012 (02:45:15))
Sí No hola me parece muy bueno tu aporte pero te cuento que no me funciona al paarecer no funciona el enlace con ajax solo le sale el link del archivo html y nada mas
Community Builder Avatar
cybnet
(14.01.2012 (10:01:03))
Sí No Saludos sebastian, por lo que tengo entendido no se puede hacer un ajax via POST con JSONP de ninguna forma, simplemente porque no es un AJAX pues no utiliza XMLHttpRequest sino que es un script dinámico que carga código javascript.
Tendrás que ingeniartelas de alguna otra forma, por ejemplo hacer la solicitud a un script en tu servidor y que sea este el que realice la solicitud al otro servidor y no desde el navegador. Yo no he encontrado niguna solución fácil y sencilla. Te dejo algunos enlaces que te pueden ser de utilidad:

-http://forum.jquery.com/topic/jquery-jquery-ajax-cross-domain-jsonp-via-post
-http://www.ajax-cross-domain.com/
-http://stackoverflow.com/questions/298745/how-do-i-send-a-cross-domain-post-request-via-javascript
Community Builder Avatar
sebastian
(14.01.2012 (02:21:05))
jsonp y POST Sí No no pude usar jsonp cuando tengo un archivo php que usa POST, la única solución fue usar:

dataType : 'text', crossDomain : true,

pero estoy seguro que esto puede traer algún inconveniente a pesar de que funcione.


Te agradecería si pudieras responderme a mi email, puedo enviarte mi script.

Muchas gracias.
Community Builder Avatar
cybnet
(21.11.2011 (00:49:51))
Sí No lo del foreach para el ejemplo puesto no es necesario, aunque hubiese quedado más completo.
Respecto a que no te funcione co getJSON (no getJson), ¿me puedes decir qué código excatamente has usado? Algo así debería funcionar:

Código :
$.getJSON("http://www.dominio.com/pages.php?callback=?",{undato:'valordato,otrodato:'valorotrodato'},function(data){....});
Community Builder Avatar
Juan Pablo Paternina
(20.11.2011 (19:59:53))
Gracias Sí No Hola amigo he probado y me ha funcionado correctamente .. te falto el foreach para cuando son varios datos los que vienen, de la base de datos.. y otra cosa me funciono con Ajax pero con getJson no me funciona como si no aceptara el callback! Gracias Saludos!!
Community Builder Avatar
Marty
(06.11.2011 (03:34:34))
Great! Sí No Man you're a genious!! this is what i've been looking for! thks a lot!!
Community Builder Avatar
cybnet
(29.06.2011 (16:20:02))
Sí No Gracias x-jim por avisarme del error al escribir!! Ya lo he corregido.
Community Builder Avatar
x-jim
(29.06.2011 (13:02:17))
Sí No Alerta en la linea 21 del archivo php,

$josondata['id'] = $row->id;

debería ser:

$jsondata['id'] = $row->id;

Smileys

:confused::cool::cry::laugh::lol::normal::blush::rolleyes::sad::shocked::sick::sleeping::smile::surprised::tongue::unsure::whistle::wink: