JSONP ci libera dal dominio

L’introduzione di AJAX ha permesso un vero salto di qualità nella realizzazione di siti dinamici dando la possibilità di creare dei client implementati con javascript che dialogano con il server senza essere costretti per ogni request a ricaricare l’intera pagina. Vi sono delle situazioni (campi di lookup, help, liste, ..) per le quali l’uso di AJAX è di fatto indispensabile.
Nei browser per motivi di sicurezza ( Same Policy Origin) è impossibile effettuare chiamate verso domini diversi da utilizzato per caricare la pagina.
In linea di massima questa limitazione non costituisce un problema nella progettazione di un sito, ma vi sono delle situazioni nelle le quali sarebbe estremamente comodo se non necessario violare tale vincolo.
Valga per tutte il caso di una lettura di un’immagine da Flickr. In ogni caso può sempre essere utile poter reperire dati da un dominio diverso specialmente quando si ha la possibilità di modificare il codice del client ma non quello del server.
Esiste però la possibilità di aggirare l’ostacolo sfruttando il tag <script> usato per caricare i file javascript. Infatti per tale tag non esiste nessun vincolo per il dominio di provenienza.
Precondizione per l’uso di questa tecnica è l’uso di JSON per incapsulare i dati, ma questo non è davvero un problema in quanto tale notazione è ormai uno standard nel dialogo client-server.
Il trucco è veramente geniale.
Il dominio sia:

http://www.altrodominio.org

il JSON da leggere:

{
"cognome": "Bruno",
"nome": "Giordano",
"datanasc": "01-01-1548",
"sesso": "m",
"luogonasc": "Nola"
}

Il server incapsula il JSON come argomento di una funzione “FnEsempio”

FnEsempio({
"cognome": "Bruno",
"nome": "Giordano",
"datanasc": "01-01-1548",
"sesso": "m",
"luogonasc": "Nola"
});

Funzione di CallBack che ha come argomento i dati in formato JSON.
Fate attenzione che la funzione, purtroppo, deve essere globale.

FnEsempio = function (data) {
alert(data);
/*
o quello che diavolo volete voi
*/
};

script creato dinamicamente .

var script = document.createElement("script");
script.setAttribute("type", "text/javascript");
script.setAttribute("src","http://www.altrodominio.org/jsonp_esempio");
document.head.appendChild(script);
document.head.removeChild(script);

Se il server usa una sua funzione di CallBack bisogna conoscerne il nome ed
utilizzare quello.
Se previsto, come nelle API di Flickr, è possibile passare come parametro al server il nome della vostra funzione di CallNack.
Se la funzione si chiamaFnMia l’URL diventa:

script.setAttribute("src","http://www.altrodominio.org/jsonp_esempio?jsoncallback=FnMia");

Il meccanismo è molto semplice: quando viene caricato lo script è eseguita la funzione di CallBack alla quale è passato come argomento il JSON che contiene i dati. Quindi  all’interno di tale funzione, da voi implementata,  avrete a vostra diposizione  i dati.
Non è necessario scrivere tutto il codice usato sopra nell’esempio. Una soluzione è disponibile in jQuery e la trovate nel file getjson.js :

$.ajax({
type: 'GET',
cache: false,
url: www.altrodomnio.org,
dataType: 'jsonp',
contentType: "application/json",
jsonpCallback: FnMia,
data: params,
success: fnOk,
error: fnErr
});

Una soluzione che funziona con la stessa logica ma non utilizza jQuery la trovate nel file uajsonp.js:

var UaJSONP = {
  load: function(url) {
  var script = document.createElement("script");
  script.setAttribute("type", "text/javascript");
  script.setAttribute("src", url);
  document.head.appendChild(script);
  document.head.removeChild(script);
  },
  call: function(url, callBack, fnOk) {
  eval(callBack + '=function(data){fnOk(data);' + callBack + '=null;}');
  UaJSONP.load(url);
  }
};

Infine vi propongo un’altra soluzione che ha il difetto di non essere standard rispetto alle API di siti tipo Flickr ma che è forse più semplice (questa è la mia impressione). Per quanto la cosa non sia rilevante credo anche che sia originale. Invece di incapsulare il JSON come argomento di una funzione, lo si trasforma in variabile con un  nome. Dato il solito JSON:

{
"cognome": "Bruno",
"nome": "Giordano",
"datanasc": "01-01-1548",
"sesso": "m",
"luogonasc": "Nola"
}

Diventa:

var nomeEsempio={
"cognome": "Bruno",
"nome": "Giordano",
"datanasc": "01-01-1548",
"sesso": "m",
"luogonasc": "Nola"
}

Vi faccio notare che è meno invasiva la modifica del JSON ma soprattutto che NON è necessario usare una variabile globale come nel caso di una funzione di CallBack. Il codice di questa soluzione lo rovate nel file uajsonp2.js.

var UaJSONP2 = {
    get: function(url, jsonName, fnCall) {
        this.fnCall = fnCall;
        this.jsonName = jsonName;
        var script = document.createElement("script");
        script.setAttribute("type", "text/javascript");
        script.setAttribute("onload", "UaJSONP2.callback()");
        script.setAttribute("src", url);
        document.head.appendChild(script);
        document.head.removeChild(script);
    },
    callback: function() {
        var f = new Function("pars", "return " + this.jsonName + ";");
        this.fnCall(f());
    }
};

Nell’esempio es11 il file es11.json:

var jsondata = {
    "esempio": "es11",
    "cognome": "Martini",
    "nome": "Roberto",
    "datanasc": "24-02-1980",
    "sesso": "m",
    "luogonasc": "Firenze"
}

Codice javascript:

var callback = function (data) {
                 console.log(data);
                };
UaJSONP2.get('http://127.0.0.1:8081/es11.json', 'jsondata', callback);

Gli esempi (uajsonp_es.tar.gz) non sono molto commentati e la grafica è spartana, ma sono contemplati diversi casi sia per quanto riguarda la soluzione adottata sia per la tipologia di dati; inoltre il codice è semplice. Gli esempi es01 e es10 utilizzano codice javascript senza le librerie, per meglio evidenziare la tecnica usata; es07 si connette a Flickr. Per provare gli esempi è disponibile un banale server in python chiamato con due porte diverse dagli script server8080.sh e server8081.sh (la roba in bat e cmd potete farvela da soli).

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *