miércoles, 22 de abril de 2015
Jquery - JSONP, llamadas AJAX entre dominios
Introducción
Seguro que alguna vez os ha pasado: intentáis hacer una petición AJAX a un dominio distinto al que estáis trabajando y os devuelve un error. En concreto el error que da Firefox es:
Access to restricted URI denied” code: “1012
Esto es normal ya que por seguridad, los navegadores no permiten hacer este tipo de llamadas. ¿Entonces, por qué hay APIs que exponen sus datos en JSON? Bueno para esta pregunta existen al menos dos respuestas:
- La API puede ser igualmente consumida por cualquier tipo de programa que no sea un navegador, como un script PHP.
- Realmente existe un truco para poder acceder a eses datos desde Javascript. Es un nuevo concepto llamado JSONP.
Vamos a ver con más detalle qué es JSONP y como siempre, ejemplos de uso.
Información sobre JSONP
JSONP es el acrónimo de JavaScript Object Notation with Padding, es decir, una forma deextension de JSON para soportar llamadas entre dominios. Según la wikipedia, este término fue propuesto en el blog MacPython en el año 2005 y desde entonces comenzó a ser usado por todo el entorno de aplicaciones web 2.0.
El funcionamiento en el que se basa, es que en nuestro código HTML sí podemos cargar un script de un dominio remoto, de forma que si tenemos nuestro dominio ontuts.com, podemos hacer perfectamente lo siguiente:
- <script type="text/javascript" src="http://www.dominioremoto.com/datos.js"></script>
De forma que si dicho script nos devuelve datos en formato JSON, los podremos recibir sin ningún problema. Pero, ¿qué pasa con eses datos? ¿cómo puedo acceder a ellos? Pues no puedes, simplemente recibes informacion pero no puedes acceder a ella.
JSONP se basa en la capacidad que tienen los navegadores de añadir scripts de otros dominios para acceder a la información.
Sin embargo, el método JSONP implica un poco de colaboración extra de nuestro servidor de datos, de forma que el servidor se tiene que encargar de meter dichos datos como parámetro de una función que sí existe en nuetro código. Es posible que andes un poco perdido, y seguramente con un ejemplo lo veas mucho mejor, de forma que vamos a crear un pequeño código que extraiga información de la API de flickr
Cómo funciona: Accediendo a la API de flickr
Si navegáis un poco por la referencia de la API de flickr, podréis encontrar fácilmente unejemplo de URL para recibir datos en formato JSON.
Si accedéis al ejemplo, veréis que nos devuelve algo como esto (puede que cambien la API Key por seguridad, de todas formas la podéis ver desde la sección ejemplos de su documentación):
- jsonFlickrApi({"method":{"_content":"flickr.test.echo"}, "format":{"_content":"json"}, "api_key":{"_content":"85f336549dc99ecaddc7d6cdea76b4b8"}, "stat":"ok"})
Ya os imagináis qué ocurriría si incluímos esta URL(script) en nuestro head del código HTML, ¿no?. Se llamará a la función jsonFlickrApi y de parámetro tendríais toda la información en formato JSON. De esta forma, conseguimos extraer datos del servidor remoto y ademásprocesarlos, que es lo que realmente nos interesa.
Todos los servidores que expongan API en JSON, deberían (o deben) aceptar un parámetro GET en el cual le especificamos cual será en nombre de la función recibidora en nuestro código local, para que pueda ser más dinámico y personalizable. En el caso de flickr este parámetro se llama jsoncallback.
Si un servidor quiere exponer JSON para aplicaciones Javascript, debe de permitirteespecificar el nombre de tu función mediante un parámetro en la URL.
De modo que si accedemos a la siguiente URL: http://www.flickr.com/services/rest/?method=flickr.test.echo&format=json&api_key=fb3db427da4bcda80f74ea31c64cd64d&jsoncallback=mifuncion
Obtendremos lo siguiente:
- mifuncion({"method":{"_content":"flickr.test.echo"}, "format":{"_content":"json"}, "api_key":{"_content":"85f336549dc99ecaddc7d6cdea76b4b8"}, "jsoncallback":{"_content":"mifuncion"}, "stat":"ok"})
En el caso de flickr, también nos permite añadir el parámetro nojsoncallback=1 con el cual sólo nos devolverá los datos, pero esto no nos valdrá de nada en Javascript, porque como he comentado antes, los datos se reciben y…ahí se quedan, no se pueden tratar de ninguna forma.
Crear una llamada AJAX a un servidor remoto
Bien, ahora que conocemos y entendemos la teoría, ha llegado la hora de la práctica. Como ya sabréis, las llamadas se van a hacer gracias a la inserción de etiquetas <script> (Javascript) que nos permiten descargar contenido de servidores remotos. Sabiendo esto, un código sencillo podría ser el siguiente:
- function jsonp(url){
- var head = document.getElementsByTagName("head")[0];
- var script = document.createElement("script");
- script.type = "text/javascript";
- script.src = url;
- head.appendChild(script);
- }
- function json_process(data){
- alert(data);
- console.info(data);
- }
- function test(){
- var url = "http://www.flickr.com/services/rest/?method=flickr.test.echo&format=json&api_key=fb3db427da4bcda80f74ea31c64cd64d&jsoncallback=json_process";
- jsonp(url);
- }
De forma que si ahora llamamos a la función test() en el evento load de la página, recibiremos los datos del dominio de flickr, este sería nuestro ejemplo Hola Mundo (sin Hola mundo ).
A continuación vamos a crear un código que pueda ser reusable y más completo.
Mejorando nuestro código
El ejemplo anterior estaba bien para un primer contacto, sin embargo, en una aplicación real, este código sería bastante malo y difícil de mantener, por eso vamos a añadir las siguientes mejoras:
- Permitir especificar la callback como parámetro de la función
- Permitir especificar cuál es el nombre del parámetro en el cual se especifica la callback a llamar, en el caso de flickr es jsoncallback
- Añadir parámetros adicionales a la URL de forma sencilla
- Encapsular el código en el espacio de nombres JSONP (‘global variables are evil‘)
¡Pues manos a la obra! Antes de nada os pongo el esquema para luego ir rellenándolo poco a poco:
- var JSONP = {
- /*Guarda la referencia al script*/
- script: null,
- /*Guarda las opciones especificadas*/
- options: {},
- /*Realiza la llamada a la url especificada siguiendo las opciones pasadas*/
- call: function(url, options){
- },
- /*Recibe el resultado*/
- process: function(data) {
- }
- };
El método call
Es el que se encarga de enviar una petición al servidor siguiendo los parámetros que le especificamos. El segundo parámetros debe de ser un objeto con las siguientes propiedades:
- callback: el nombre de la función a ejecutar cuando llege la respuesta
- callbackParamName: nombre del parámetro GET que define el nombre de la función a llamar (recuerda jsoncallback en el caso de flickr)
- params: un objeto del tipo clave-valor que será serializado e incrustado en la URL
Un ejemplo del objeto options es:
- var options = {
- callback: mifuncion,
- callbackParamName: "jsoncallback",
- params: { a=1, b=2}
- };
Y este sería el código del método:
- call: function(url, options){
- //Comprobacion de las opciones
- if(!options) this.options = {};
- this.options.callback = options.callback || function(){};
- this.options.callbackParamName = options.callbackParamName || "callback";
- this.options.params = options.params || [];
- //Determina si se debe añadir el parámetro separado por ? o por &
- var separator = url.indexOf("?") > -1? "&" : "?";
- /*Serializa el objeto en una cadena de texto con formato URL*/
- var params = [];
- for(var prop in this.options.params){
- params.push(prop + "=" + encodeURIComponent(options.params[prop]));
- }
- var stringParams = params.join("&");
- //Crea el script o borra el usado anteriormente
- var head = document.getElementsByTagName("head")[0];
- if(this.script){
- head.removeChild(script);
- }
- script = document.createElement("script");
- script.type = "text/javascript";
- //Añade y carga el script, indicandole que llame a JSONP.process
- script.src = url + separator + stringParams + (stringParams?"&":"") + this.options.callbackParamName +"=JSONP.process";
- head.appendChild(script);
- }
Supongo que no hay mucho que explicar a mayores del código, puesto que simplemente mejora la base anterior, pero la lógica apenas cambia.
El método process
Este es la función que nuestro código fuerza a ejecutar una vez cargados los datos, aquí se pueden hacer tareas comunes de deserialización de datos, comprobación de errores, etc. En nuestro caso sólo sirve de puente entre el servidor y la callback especificada.
- process: function(data) {
- /*Aquí pueden hacerse tareas comunes de tratamiento de los datos*/
- this.options.callback(data);
- }
Una vez vistos los métodos, vamos a probarlo.
Probando el código
Ahora que ya tenemos definida nuestra pequeña clase, podemos hacer llamadas a dominos remotos de una forma muy sencilla:
- function test(){
- var url = "http://www.flickr.com/services/rest/?method=flickr.test.echo&format=json&api_key=fb3db427da4bcda80f74ea31c64cd64d";
- var params = {
- callback: function(data){ alert(data);},
- callbackParamName: "jsoncallback",
- };
- JSONP.call(url, params);
- }
O aprovechando al máximo nuestro código:
- function test(){
- var url = "http://www.flickr.com/services/rest";
- var params = {
- callback: function(data){ alert(data);},
- callbackParamName: "jsoncallback",
- params: {
- method: "flickr.test.echo",
- format: "json",
- api_key: "fb3db427da4bcda80f74ea31c64cd64d"
- }
- };
- JSONP.call(url, params);
- }
¿Verdad que así queda mucho más curioso y limpio? Además de ser un código mucho más fácil de reutilizar, ya que lo puedes añadir a cualquier proyecto y seguiría funcionando perfectamente.
JSONP en jQuery
La verdad que ni me he molestado en buscar plugins para incluír JSONP en jQuery (de hecho nuncha he tenido la necesidad real de utilizar JSONP). Pero ya que hace un par de semanas os explicaba cómo crear un plugin de jQuery, he decidido convertir el código anterior y así, predicar con el ejemplo
- (function($){
- $.extend(
- {
- jsonp: {
- script: null,
- options: {},
- call: function(url, options) {
- var default_options = {
- callback: function(){},
- callbackParamName: "callback",
- params: []
- };
- this.options = $.extend(default_options, options);
- //Determina si se debe añadir el parámetro separado por ? o por &
- var separator = url.indexOf("?") > -1? "&" : "?";
- var head = $("head")[0];
- /*Serializa el objeto en una cadena de texto con formato URL*/
- var params = [];
- for(var prop in this.options.params){
- params.push(prop + "=" + encodeURIComponent(options.params[prop]));
- }
- var stringParams = params.join("&");
- //Crea el script o borra el usado anteriormente
- if(this.script){
- head.removeChild(script);
- }
- script = document.createElement("script");
- script.type = "text/javascript";
- //Añade y carga el script, indicandole que llame al metodo process
- script.src = url + separator + stringParams + (stringParams?"&":"") + this.options.callbackParamName +"=jQuery.jsonp.process";
- head.appendChild(script);
- },
- process: function(data) { this.options.callback(data); }
- }
- });
- })(jQuery)
Por tanto ahora también podréis hacer lo siguiente (suponiendo que la librería jQuery está incluída):
- function test(){
- var url = "http://www.flickr.com/services/rest";
- var params = {
- callback: function(data){ alert(data);},
- callbackParamName: "jsoncallback",
- params: {
- method: "flickr.test.echo",
- format: "json",
- api_key: "fb3db427da4bcda80f74ea31c64cd64d"
- }
- };
- $.jsonp.call(url, params);
- }
Como siempre con jQuery nos queda todo muy fácil de utilizar.
Suscribirse a:
Enviar comentarios
(
Atom
)
Sígueme en las Redes Sociales
Donaciones
Datos personales
Entradas populares
-
En este apartado vamos a explicar como ejercutar archivos PHP a través del terminal de Ubuntu. Lo primero que tendríamos que hacer es inst...
-
En este blog voy a comentar un tema que se utilizan en casi todas las páginas web que existen, y es el tema de la paginación. La paginaci...
-
Este post trata de la integración de la librería PHPExcel en Codeigniter, aunque se podría aplicar a cualquier librería, como por ejemplo mP...
-
Ejemplo para añadir o sumar un número determinado de hora/s, minuto/s, segundo/s a una fecha en php. Con la función strtotime se puede ...
-
Este tema es uno de los temas primordiales sobre el framework Codeigniter, ya que en alguna ocación nos hemos visto obligados a recoger dato...
© Espacio Daycry - Espacio de programación 2013 . Powered by Bootstrap , Blogger templates and RWD Testing Tool
No hay comentarios :
Publicar un comentario