Sicuramente quanche volta vi è successo: si tenta di fare una richiesta AJAX in un dominio diverso da quello che si sta lavorando e restituisce un errore. In particolare, l’errore che dà Firefox è il seguente: Access to restricted URI denied” code: “1012
Questo è normale, perché per ragioni di sicurezza, i browser non consentono tali chiamate.
Allora, perché ci sono le API che espongono i loro dati in JSON? Bene per questa domanda ci sono almeno due risposte:
- L’API può anche essere utilizzata da qualsiasi programma diverso da un browser web, come uno script in php, jsp o asp.
- C’è davvero un trucco per accedere ai dati da Javascript. Si tratta di un nuovo concetto chiamato JSONP.
Vediamo in dettaglio cosa è JSONP, e come sempre, con qualche esempio di utilizzo.
[nextpage title=”Informazioni su JSONP”] JSONP sta per JavaScript Object Notation with Padding, cioè una forma di estensione JSON per supportare chiamate tra domini. Secondo wikipedia, questo termine è stato proposto nel blog MacPython nel 2005 e da allora ha cominciato ad essere utilizzato su tutti gli ambienti con applicazioni web 2.0.Il funcionamento in cui essa si fonda, è che con il nostro codice HTML siamo in grado di caricare uno script da un dominio remoto, in modo che se abbiamo un nostro dominio miodominio.com, siamo in grado di fare perfettamente il seguente:
<script type="text/javascript" src="http://www.dominioremoto.com/dati.js"></script>
JSONP si basa sulla capacità dei browser di aggiungere dei script da altri domini per accedere a tali informazioni.
Tuttavia, il metodo JSONP comporta un po’ di collaborarione aggiuntiva dal nostro server di dati, nel senso che il server dovrà occuparsi di mettere questi dati come parametri in una funzione che esiste nel nostro codice client. È possibile che ti sia un po’ perso, ma sicuramente con un esempio si può vedere meglio, quindi cerchiamo di creare un piccolo codice per estrarre informazioni attraverso l’API di flickr. Come funziona: Accesso alle API di Flickr
Se si accede all’esempio, vedrete che tornerà qualcosa del genere (è possibile che la chiave API cambi, per sicurezza; in ogni caso si può vedere dalla sezione di esempi della sua documentazione):
jsonFlickrApi({
"method":{
"_content":"flickr.test.echo"
},
"format":{
"_content":"json"
},
"api_key":{
"_content":"ca961e5f59eebb3339a87c168e390998"
},
"stat":"ok"
});
Tutti i server che espongono le API in formato JSON, dovrebbero (o devono) accettare un parametro GET in cui si specificherà quale sarà in nome della funzione ricevitrice nel nostro codice locale, in modo da poter essere più dinamico e personalizzabile. Nel caso di flickr questo parametro si chiama jsoncallback.
Se un server vuole esporre JSON per applicazioni Javascript, deve permettere di specificare il nome della tua funzione mediante un parametro nell’URL.
Quindi, se accediamo al seguente URL: http://www.flickr.com/services/rest/?method=flickr.test.echo&format=json&api_key=fb3db427da4bcda80f74ea31c64cd64d&jsoncallback=miaFunzione
Otterremo il seguente codice
miaFunzione({
"method":{
"_content":"flickr.test.echo"
},
"format":{
"_content":"json"
},
"api_key":{
"_content":"85f336549dc99ecaddc7d6cdea76b4b8"
},
"jsoncallback":{
"_content":"miaFunzione"
},
"stat":"ok"
});
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);
}
Poi creeremo un codice che possa essere riutilizzabile e più completo.
- Permettere di specificare la callback come parametro della funzione
- Permettere di specificare qual è il nome del parametro nel quale si specifica la callback da richiamare in automatico, in caso che flickr sia jsoncallback
- Aggiungere ulteriori parametri all’URL in modo semplice
- Incapsulare il codice nello spazio dei nomi JSONP
(‘le variabili globali sono un male‘)
Quindi, al lavoro! Prima di tutto ho messo lo schema per poi andare a riempirlo mano a mano:
var JSONP = {
// Salva il riferimento allo script
script: null,
// Salva le opzioni specificate
options: {},
// Effettua la chiamata all'url specificato
// seguendo le opzioni passate
call: function(url, options){
},
// Riceve il risultato
process: function(data) {
}
};
- callback: Il nome della funzione da eseguire quando arriva la risposta
- callbackParamName: nome del parametro GET che definisce il nome della funzione da chiamare (ricorda jsoncallback nel caso di Flickr)
- params: un oggetto del tipo chiave-valore da serializzare e incorporarlo nell’URL
Un esempio dell’oggetto options sarebbe:
var options = {
callback: miaFunzione,
callbackParamName: "jsoncallback",
params: {
a=1,
b=2
}
};
call: function(url, options){
// Verifica le opzioni
if(!options) this.options = {};
this.options.callback = options.callback || function(){};
this.options.callbackParamName = options.callbackParamName || "callback";
this.options.params = options.params || [];
// Determina se aggiungere un parametro
// separato da ? o da &
var separator = url.indexOf("?") > -1? "&" : "?";
// Serializza l'oggetto in una stringa di testo
// con formato URL
var params = [];
for(var prop in this.options.params){
params.push(prop + "=" + encodeURIComponent(options.params[prop]));
}
var stringParams = params.join("&");
// Crea lo script o cancella l'utilizzato in precedenza
var head = document.getElementsByTagName("head")[0];
if(this.script){
head.removeChild(script);
}
script = document.createElement("script");
script.type = "text/javascript";
// Aggiunge e carica lo script,
// indicando di chiamare JSONP.process
script.src = url + separator + stringParams + (stringParams?"&":"") + this.options.callbackParamName +"=JSONP.process";
head.appendChild(script);
}
process: function(data) {
// Qui si possono fare
// le operazioni comuni per trattare i dati
this.options.callback(data);
}
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);
}
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);
}
(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 se aggiungere un parametro
// separato da ? o da &
var separator = url.indexOf("?") > -1? "&" : "?";
var head = $("head")[0];
// Serializza l'oggetto in una stringa di testo
// con formato URL
var params = [];
for(var prop in this.options.params){
params.push(prop + "=" + encodeURIComponent(options.params[prop]));
}
var stringParams = params.join("&");
// Crea lo script o cancella l'utilizzato in precedenza
if(this.script){
head.removeChild(script);
}
script = document.createElement("script");
script.type = "text/javascript";
// Aggiunge e carica lo script,
// indicando di chiamare il 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);
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);
}
Spero che il codice che ho condiviso vi aiutino o addirittura vi motivino a creare applicazioni basate su questo tipo di API, giacché è davvero interessante per essere asincrona e per permettere la comunicazione con un altro server direttamente senza passare dal nostro.
Fino alla prossima volta!

Ancora nessun commento