JSONP e jQuery: conosciamoli meglio

Negli ultimi anni il formato JSON si sta conquistando la simpatia di tutti: si legge bene, non è verboso (quindi leggero), e va a braccetto con JavaScript. Molte delle API web che si trovano in giro per interagire con i social network per esempio, utilizzano questo formato di scambio dati. Meno nota, o confusa con esso, è invece la notazione JSONP (che sta per JavaScript Object Notation with Padding) che spesso usiamo forse inavvertitamente se lavoriamo per esempio con le API di Twitter o Facebook lato client. Ma che significa? Perché mi dovrebbe interessare la differenza?

Chiamate Cross Domain

Per una policy di sicurezza implementata nei browser (chiamata Same Policy Origin) non è possibile eseguire operazioni sul DOM da script cross-domain, come per esempio effettuare chiamate ajax verso domini diversi da quello da cui proviene la pagina (anche se in HTML5 non è poi più così vero al 100%…). Allora, come visualizzare gli ultimi post di Cose Non Javiste sul vostro sito? 🙂

Un’eccezione a questa regola sono i tag che recuperano le risorse legate ad una pagina web, come il tag <script/>. Ma allora di cosa stiamo parlando, di JavaScript o di JSON? In realtà di entrambi… Ma facciamo ordine!

P come Padding

Secondo wordreference.com, padding significa imbottitura, riverstimento. In cosa consiste allora il pattern JSONP? Come dice la parola stessa, non fa altro che rivestire il JSON, che magari risiede in un altro dominio, con codice JavaScript, in modo che possa essere chiamato da un tag <script/> (in modo non più Ajax quindi). Il problema allora è: come faccio a rivestire di JavaScript, per esempio, le risorse JSON esposte dalla API di Cose Non Javiste? Qua entrano in gioco i browser che supportano questo pattern tramite le parole chiave jsonp o callback usate come query param. Un esempio è meglio di mille parole:

<script type="text/javascript">
function recentPosts(data) {
  alert("I post recenti sono "+ data.count);
}
</script>
<script type="text/javascript"
        src="http://www.cosenonjaviste.it/api/get_recent_posts?callback=recentPosts">
</script>

Come è facile intuire, il browser chiamerà la funzione di callback recentPosts(data) definita nel nostro DOM (occhio che la funzione è definita prima della chiamata allo script!) passando i dati JSON recuperati da Cose Non Javiste. count infatti è un attributo dell’oggetto data ritornato dal servizio. Per conoscere la struttura di data, basta loggarlo nella console del browser e recuperare le informazioni che ci interessano!
Se ispezioniamo il body della richiesta (con Firebug per esempio), possiamo vedere la nostra “imbottitura”:

recentPosts({/*JSON data from CNJ*/})

La cosa particolare di questa tecnica è che il codice JSON che siamo recuperando, in realtà lo andiamo a chiamare come content type text/javascript e non application/json per via dell’uso del tag <script/>. Questo è possibile perché la API di Cose Non Javiste è capace di interpretare il parametro callback e restituire la risorsa attorno alla chiamata alla nostra funzione JavaScript! A ben guardare quindi il browser è meno furbo di quanto sembrava all’inizio: semplicemente esegue la nostra funzione restituita da Cose Non Javiste.

Imbottitura con jQuery

jQuery, grazie ai tutorial di Gianni, ormai lo conoscete. Tra le mille cose che fa, permette di semplificare anche la gestione delle chiamate JSONP. Prendiamo per esempio la funzione $.getJSON: è una scorciatoia per recuperare dati JSON con richieste AJAX. Nel nostro caso però è necessario “attivare” la modalità JSONP, aggiungendo il parametro callback come segue:

var recentPostsUrl = 'http://www.cosenonjaviste.it/api/get_recent_posts?callback=?';
$.getJSON(recentPostsUrl, function(data) {
  alert("I post recenti sono "+ data.count);
});

Da notare il parametro callback=?: la funzione $.getJSON in questo caso eseguirà una chiamata HTTP GET a CNJ, sostituendo il valore “?” con un riferimento alla funzione anonima chiamata in callback. Quindi: il risultato è lo stesso ed è proprio in slogan jQuery “scrivi meno, fai di più”! Se omettiamo il parametro callback=? o diamo un valore al parametro, $.getJSON proverà a fare una chiamata AJAX che in questo caso il browser bloccherà. Chrome per esempio scrive in console: “No ‘Access-Control-Allow-Origin’ header is present on the requested resource“.

Se proprio vogliamo o dobbiamo dare alla callback un nome, questa strada non è più percorribile. Però è facile trovare l’alternativa: l’importante è chiamare il servizio con content type text/javascript con il valore della funzione di callback:

var recentPostsUrl = 'http://www.cosenonjaviste.it/api/get_recent_posts?callback=recentPosts';

$.getScript(recentPostsUrl);

function recentPosts(data) {
  alert("I post recenti sono "+ data.count);
}

L’esempio completo

Di seguito il codice HTML per testare immediatamente JSONP! Oltre a mostrare quanti post sono, ne elenca i titoli aggiungendo un link al post:

<!DOCTYPE html>
<html>
   <head>
      <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
      <title>CNJ Recent posts</title>
      <script type="text/javascript" charset="utf-8">
      //Se non metto callback=? il metodo jquery fa una chiamata ajax!!
      var cnjRecentPosts = 'http://www.cosenonjaviste.it/api/get_recent_posts?callback=?';

      $(document).ready(function() {
         $.getJSON(cnjRecentPosts, function(data) {
            alert("I post recenti sono "+ data.count);
            $('<ul></ul>').appendTo('#recent_posts');
            $(data.posts).each(function(i, el) {
               $('#recent_posts ul').append('<li><a href="' + el.url + '" target="_blank">' + el.title + '</a></li>');
            });
         });
      });
      </script>
   </head>
   <body>
      <div id="recent_posts">		
      </div>
   </body>
</html>

Conclusioni

Ecco svelato quindi come è possibile usare tutte le API web che espongono le risorse in formato JSON e supportano il Padding, direttamente da codice JavaScript senza scomodare il server. jQuery, come al solito, ci rende la vita più semplice… come farne a meno ormai? 😉

Andrea Como

Sono un software engineer focalizzato nella progettazione e sviluppo di applicazioni web in Java. Presso OmniaGroup ricopro il ruolo di Tech Leader sulle tecnologie legate alla piattaforma Java EE 5 (come WebSphere 7.0, EJB3, JPA 1 (EclipseLink), JSF 1.2 (Mojarra) e RichFaces 3) e Java EE 6 con JBoss AS 7, in particolare di CDI, JAX-RS, nonché di EJB 3.1, JPA2, JSF2 e RichFaces 4. Al momento mi occupo di ECM, in particolar modo sulla customizzazione di Alfresco 4 e sulla sua installazione con tecnologie da devops come Vagrant e Chef. In passato ho lavorato con la piattaforma alternativa alla enterprise per lo sviluppo web: Java SE 6, Tomcat 6, Hibernate 3 e Spring 2.5. Nei ritagli di tempo sviluppo siti web in PHP e ASP. Per maggiori informazioni consulta il mio curriculum pubblico. Follow me on Twitter - LinkedIn profile - Google+

  • Alessandro Curcu

    Ottimo! Spiegato bene! Grazie!