Capitolo 12.   Web 2.0

A partire dal 2004 si è iniziato a sentir parlare sempre più frequentemente di Web 2.0 relativamente ad una serie di nuovi servizi e applicazioni presenti in Internet.

Il termine viene però anche usato per sottolineare una nuova fase di «vitalità» della rete, successiva alla crisi della «new economy» registrata nei primi anni del nuovo millennio, nonché per sottolineare una modalità innovativa di uso delle le risorse di Internet con un ruolo più attivo da parte degli utenti.

Una possibile definizione di Web 2.0 è la seguente: «insieme delle diverse applicazioni presenti nel Web che permettono un elevato livello di interazione tra l’applicazione stessa e l’utente che ne fa uso».

Occorre notare che questa terminologia è stata proposta solo per designare l'attuale stato evolutivo del Web rispetto alla condizione precedente, per la quale, in origine, non era previsto alcun «numero di versione».

Dal momento però che il termine Web 2.0 è diventato di uso comune, si è pensato di «battezzare» con un numero di versione anche le fasi evolutive precedenti; abbiamo dunque:

Una osservazione importante riguarda la natura evolutiva della storia del Web: ogni «versione» non ha cancellato quanto già presente ma ha portato ampliamenti e miglioramenti soprattutto a livello di servizi per gli utenti.

Dal punto di vista tecnologico le basi sono rimaste immutate e sono costituite dal protocollo HTTP e dal linguaggio HTML, risalenti al Web 1.0, cui si sono via via affiancate altre tecnologie come i CSS, i CGI, i linguaggi per le elaborazioni lato servente.

Per completezza è opportuno segnalare che si parla già anche di «Web 3.0» per indicare vari possibili sviluppi futuri fra i quali quello prevalente sembra essere il Web Semantico, che qui però non approfondiamo.

Tornando al Web 2.0, è utile distinguere due punti di vista, o accezioni, per la sua descrizione:

12.1   Web 2.0: punto di vista «sociale»

A partire dal 2004 si hanno una serie di novità che giocano un ruolo decisivo per l'affermazione del Web 2.0:

Sicuramente in questi anni un ruolo fondamentale per l'affermazione del Web 2.0 è stato svolto da Google, sia per l'importanza dei servizi offerti da questa azienda, che vanno al di là del «tradizionale» motore di ricerca e che hanno rivoluzionato il mondo delle applicazioni Web, sia per la proposta della piattaforma Android, sia infine, per il lancio di Ajax, che come vedremo tra breve, è uno dei pilastri tecnologici su cui si basano le applicazioni del Web 2.0.

Tali applicazioni vengono spesso definite «beta», cioè non definitive, e siccome la loro evoluzione è continua, anche grazie all'interazione con gli utenti e fra gli utenti, si può prevedere che difficilmente raggiungeranno uno stato consolidato; questa situazione viene illustrata denominando le applicazioni del Web 2.0 come «never ending beta applications».

12.2   Web 2.0: punto di vista tecnologico

La novità tecnologica più importante degli ultimi anni per il Web è stata Ajax (Asynchronous JavaScript And XML).

Questo acronimo è stato proposto per la prima volta nel 2005 in un articolo del Jesse James Garrett, intitolato: «Ajax: A New Approach to Web Applications», in cui spiega il nuovo approccio per le applicazioni Web che Google aveva iniziato ad usare (e che ancora usa), nelle piattaforme Google Suggest e Google Maps.

Secondo la definizione di Garrett Ajax non è una nuova tecnologia, ma un aggregato di più tecnologie già esistenti, le quali, se usate in sinergia, permettono di aumentare l'interazione, la dinamicità e l'efficienza delle applicazioni Web.

Grazie anche ad Ajax si è avuta una massiccia evoluzione e diffusione delle cosiddette RIA (Rich Internet Application) progettate e realizzate come le corrispondenti applicazioni «desktop» e che forniscono gli stessi servizi di queste ultime, pur essendo sulla rete (Web based) e quindi usabili dagli utenti in modo diffuso e decentrato.

Il concetto di «ricchezza» in queste applicazioni va interpretato sotto due punti di vista:

Nei prossimi paragrafi vengono forniti alcuni dettagli relativi all'uso di Ajax.

12.3   Generalità su Ajax

Ajax si basa sui seguenti elementi:

  1. presentazione standard utilizzando XHTML e CSS;

  2. interazione con l’utente e visualizzazione dinamica dei contenuti tramite DOM;

  3. scambio e manipolazione delle informazioni utilizzando preferibilmente XML e XSLT;

  4. richiesta delle informazioni in maniera asincrona tramite l’oggetto JavaScript XMLHttpRequest;

  5. uso di JavaScript per «legare» insieme tutte le tecnologie sopracitate.

Con Ajax si può superare il modello classico «richiesta/risposta» delle applicazioni Web, effettuando chiamate asincrone verso il servente.

Ricordiamo infatti che il flusso tradizionale delle applicazini Web, secondo il protocollo HTTP, è il seguente:

Qualsiasi aggiornamento dei dati presentati nella risposta presuppone l'invio di ulteriori richieste da parte del cliente.

Ricordiamo inoltre che in questo tipo di applicazioni il front-end (moduli HTML e risposte) è separato anche fisicamente dalla logica applicativa (oltre che dal back-end), cosa che non avviene nei normali programmi «desktop» nei quali tutte le componenti coesistono in un'unica unità elaborativa.

Questo rende il comportamento delle applicazioni Web particolarmente complesso, lento e macchinoso.

La novità di Ajax consiste nella possibilità di effettuare chiamate asincrone verso il servente, utilizzando codice JavaScript, anche senza che l'utente faccia una richiesta esplicita tramite il programma di navigazione.

Così è possibile ad esempio aggiornare alcuni dati presenti sulla pagina senza necessità di ricaricarla interamente.

Gli schemi delle figure 12.1 e 12.2 illustrano il comportamento delle applicazioni Ajax paragonandolo a quello delle applicazioni Web tradizionali.

Figura 12.1.

figure/pagine-web-schema-ajax1

Figura 12.2.

figure/pagine-web-schema-ajax2

Il cardine di tutto il meccanismo è l'oggetto JavaScript XMLHttpRequest grazie al quale si effettuano richieste sincrone e asincrone sfruttando il protocollo HTTP.

Lo scambio di dati che si può realizzare con questo oggetto sarebbe da effettuare, secondo chi ha coniato l'acronimo Ajax, usando il formato XML; in realtà ciò non è assolutamente obbligatorio e si possono utilizzare molti altri formati fra cui il testo puro.

Grazie ad Ajax la pagina Web può divenire un'entità attiva, dinamica e fortemente responsiva con un comportamento interattivo simile a quello delle applicazioni «desktop»; ecco quindi che sempre più spesso anche nel campo delle applicazioni Web si sente parlare di oggetti come «controlli», «eventi», «finestre» ed altri, tipici della programmazione tradizionale.

Con Ajax la navigazione può essere più piacevole, dinamica e coinvolgente per l'utente; inoltre buona parte dell'elaborazione viene spostata nel cliente alleggerendo il carico del servente e diminuisce anche la quantità di dati da scambiare non essendo più necessario richiedere intere pagine.

Ci sono però anche alcuni svantaggi o problemi da tenere in considerazione:

12.4   L'oggetto XMLHttpRequest

Storicamente l'oggetto XMLHttpRequest trova origine nel pacchetto di supporto a XML introdotto da Microsoft nel browser Internet Explorer 5 e precisamente da un componente Activex chiamato XMLHttp.

Successivamente anche tutti gli altri programmi di navigazione hanno seguito questa strada, privilegiando però il linguaggio JavaScript e adottando l'oggetto XMLHttpRequest.

Dalla versione 7 del suo browser, anche Microsoft supporta questo oggetto e questo è sicuramente un beneficio per gli sviluppatori che non sono più costretti a usare oggetti diversi per creare applicazioni Ajax funzionanti con i più diffusi programmi di navigazione.

Per usare l'oggetto XMLHttpRequest occorre compiere le seguenti operazioni fondamentali:

Vediamole in dettaglio:

12.4.1   Chiamate di tipo get

Vediamo adesso un listato che riepiloga quanto illustrato finora eseguendo una richiesta di tipo get con lo scopo di ricevere il file eseAjax.html> al caricamento della pagina Web in cui il codice JavaScript è inserito:

      1 <html>
      2 <head><title>Esempio Ajax</title> 
      3 <script language="JavaScript" type="text/javascript"> 
      4    <!-- Begin script 
      5    var oggXHR = new XMLHttpRequest();
      6    function funzCallback() {
      7       try {
      8          if (oggXHR.readyState == 4) { // risposta completa
      9             if (oggXHR.status == 200) {
     10                risTesto = oggXHR.responseText; 
     11                document.getElementById('carica').innerHTML = risTesto;
     12             }
     13             else {
     14                alert ('Errore HTTP');
     15             }
     16          }
     17       } catch (e) {
     18         alert ('Errore nella funzione di callback: ' + e);
     19         }
     20    }
     21    function provaAjax() {
     22       oggXHR.onreadystatechange = funzCallback;
     23       oggXHR.open("get","eseAjax.html", true);
     24       oggXHR.send();      
     25    }                
     26    // --> 
     27 </script>
     28 </head> 
     29 <body onLoad="provaAjax()";> 
     30 <div id="carica" style="text-align:center;">
     31 Ajax non funziona!
     32 </div> 
     33 </body> 
     34 </html> 

Al fine di verificare il funzionamento di quanto esposto, è opportuno ricordare che Ajax si appoggia comunque al protocollo HTTP e quindi la pagina deve essere richiesta ad un servente Web (eventualmente http://localhost) e non aperta con file:// o con la scelta «Apri file» del menu «File».

Soffermiamoci sulle parti salienti del listato:

12.4.2   Chiamate di tipo post

Con una chiamata di tipo post si può richiedere una risposta al servente inviandogli contemporaneamente dei dati, da inserire nel corpo della richiesta, come argomento del metodo send.

Affinché l'invio dei dati funzioni correttamente è necessario che:

Come esempio realizziamo un modulo con un singolo campo in cui inserire il nome di una località che viene inviato con una chiamata di tipo post al servente; la risposta sarà il corrispondente codice di avviamento postale da far apparire a fianco del campo di input.

      1 <html>
      2 <head><title>Esempio2 AJAX</title> 
      3 <script language="JavaScript" type="text/javascript"> 
      4    <!-- Begin script 
      5    var oggXHR = new XMLHttpRequest();
      6    function funzCallback() {
      7       try {
      8          if (oggXHR.readyState == 4) { // risposta completa
      9             if (oggXHR.status == 200) {
     10                risTesto = oggXHR.responseText; 
     11                document.getElementById('cap').innerHTML = risTesto;
     12             }
     13             else {
     14                alert ('Errore HTTP');
     15             }
     16          }
     17       } catch (e) {
     18         alert ('Errore nella funzione di callback: ' + e);
     19         }
     20    }
     21    function provaAjax() {
     22       oggXHR.onreadystatechange = funzCallback;
     23       oggXHR.open("post","elabora.php", true);
     24       oggXHR.setRequestHeader("Content-Type",\
  \"application/x-www-form-urlencoded"); 25 var dati = "loc=" + \
  \encodeURIComponent(document.forms.modulo1.loc.value); 26 oggXHR.send(dati); 27 } 28 function pulisci() { 29 document.getElementById('cap').innerHTML = ""; 30 } 31 // --> 32 </script> 33 </head> 34 <body> 35 <div style="text-align:center;font-weight:bold;"> 36 Ricerca CAP 37 <p/> 38 <form id="modulo1"> 39 <input type="text" name="loc"/> 40 <span id="cap"></span> 41 <p /><br /> 42 <input type="button" value="Trova CAP" onClick="provaAjax();"/> 43 <input type="reset" value="Pulisci" onClick="pulisci()"/> 44 </form> 45 </body> 46 </html>

Rispetto all'esempio precedente le differenze principali sono alle righe da 23 a 26 dove viene effettuata la chiamata di tipo post, viene impostata l'intestazione della richiesta e viene fatto l'invio passando il parametro contenente il dato raccolto dal campo di input del modulo.

Il dato ricevuto in risposta (righe 10 e 11) viene inserito nel modulo a fianco del campo di input (riga 40).

Il piccolo modulo usato nell'esempio viene gestito con funzioni JavaScript, sia per l'invio della richiesta (riga 42) che per la pulizia dei dati (riga 43).

Per l'elaborazione dal lato del servente si predispone uno script PHP, elabora.php che effettua l'associazione tra codice e località usando un piccolo vettore associativo contente nomi e codici delle località Milano, Treviso e Roma (naturalmente questa soluzione non è realistica ed è proposta solo a titolo di esempio).

<?php 
$tab['Milano']="20100";
$tab['Roma']="00100";
$tab['Treviso']="31100";
$nomeLoc=$_POST['loc'];
$ris=$tab[$nomeLoc];
if ($ris=="") {
   $ris="Non trovato";
}   
print ("$ris");
?>

Nelle due figure 12.12 e 12.13 vediamo il comportamento della pagina appena realizzata.

Figura 12.12.

figure/pagine-web-ajax-ese0001

Figura 12.13.

figure/pagine-web-ajax-ese0002

12.4.3   Chiamate sincrone

Anche se non è importante nell'ambito di Ajax, il metodo XMLHttpRequest permette di effettuare anche chiamate sincrone; basta usare il metodo open passando come terzo parametro il valore false.

In questo caso non è necessario gestire l'evento onreadystatechange e quindi non serve la funzione di callback.

<html>
<head><title>Esempio Ajax sincrono</title> 
<script language="JavaScript" type="text/javascript"> 
   <!-- Begin script 
   function provaAjax() {
      var oggXHR = new XMLHttpRequest();
      oggXHR.open("get","eseAjax.html", false);
      oggXHR.send();      
      risTesto = oggXHR.responseText; 
      document.getElementById('carica').innerHTML = risTesto;
   }                
   // --> 
</script>
</head> 
<body onLoad="provaAjax()";> 
<div id="carica" style="text-align:center;">
Ajax non funziona!
</div> 
</body> 
</html> 

Questo esempio produce lo stesso risultato di quello «asincrono» mostrato nel paragrafo 12.4.1 con la differenza che il cliente, rimane bloccato in attesa della risposta per tutto il tempo necessario al servente ad elaborare e inviare la risposta stessa.

Si ha quindi un comportamento del tutto analogo a quello «classico» delle applicazioni Web tradizionali con tutte le conseguenze che da ciò discendono dal punto di vista della logica elaborativa e dei tempi di risposta della procedura.

12.4.4   Esempio di gestione di un modulo con Ajax

Per concludere questa rapida carrellata su Ajax vediamo un esempio un po' più complesso in cui viene gestito un modulo di immissione dati su una macchina cliente grazie ad una interazione «continua» con il servente che deve ricevere e controllare le informazioni.

In particolare supponiamo che il modulo serva a registrarsi ad un qualche tipo di servizio e richieda obbligatoriamente un nome utente, una password con relativa conferma, un indirizzo di posta elettronica.

La logica di funzionamento dell'interfaccia è la seguente:

Anche stavolta il motore applicativo sul servente, scritto in PHP, viene solo abbozzato utilizzando dei vettori associativi con dati predefiniti e non, come sarebbe logico, archivi gestiti dal back-end della procedura.

A differenza degli esempi dei paragrafi precedenti, la parte relativa all'interfaccia viene realizzata scrivendo le funzioni JavaScript in un file separato dal file HTML.

Analogamente agli esempi precedenti, non viene invece curato l'aspetto esteriore della pagina e dei messaggi di risposta; quindi l'uso dei CSS è ridotto al minimo indispensabile.

Iniziamo mostrando il sorgente HTML con il modulo di immissione dei dati:

<html>
<head><title>Modulo AJAX</title> 
<style type="text/css">
div.titolo     { font-weight: bold;
                }
div#risposta   {
                 color: blue;
                }
.campo         { position: absolute;
                 left: 160px;
                }
.campoCheck    { position: absolute;
                 left: 370px;
                 color: blue;
                }   
</style>
<script language="JavaScript" type="text/javascript" src="moduloAjaxFunz.js"> 
</script>
</head> 
<body> 
<div class="titolo">
Immissione dati
</div>
<p/>
<form id="modulo1">
Nome utente: <input class="campo" onblur="controlla('nomeUtente', '1');"
type="text" name="nomeUtente" id="nomeUtente"/>
<span class="campoCheck" id="campoCheck1"></span>
<br />
Password: <input class="campo" onblur="controlla('psw', '2');"
type="password" name="psw" id="psw"/>
<span class="campoCheck" id="campoCheck2"></span>
<br />
Ripeti password: <input class="campo" onblur="controlla('psw2', '3');" 
type="password" name="psw2" id="psw2"/>
<span class="campoCheck" id="campoCheck3"></span>
<br />
Indirizzo di posta: <input class="campo" onblur="controlla('indPosta', '4');" 
type="text" name="indPosta" id="indPosta"/>
<span class="campoCheck" id="campoCheck4"></span>
<p /><br />
<input type="button" value="Conferma" onClick="inviaDati();"/>
<input type="reset" value="Pulisci" onClick="pulisci('4')"/>
</form>
<div id="risposta">
</div>
</body> 
</html> 

Importante notare, all'interno del modulo, il richiamo della funzione controlla alla perdita del «focus» di ogni campo, la definizione delle aree per accogliere i messaggi di risposta di tale funzione (con uso del tag <span>), il richiamo delle funzioni inviaDati e pulisci rispettivamente alla pressione dei pulsanti di conferma e di reset.

Il file con le funzioni JavaScript e le chiamate Ajax è il seguente:

      1 var flagErr=0;
      2 function controlla(campo, numCampo) {
      3    oggXHR = new XMLHttpRequest();
      4    oggXHR.onreadystatechange = function() { // funzione di callback
      5       try {
      6       if (oggXHR.readyState == 4) { 
      7          if (oggXHR.status == 200) {
      8             campoCheck = 'campoCheck' + numCampo;
      9             risTesto = oggXHR.responseText;
     10             document.getElementById(campoCheck).innerHTML = risTesto;
     11             if (risTesto!='OK!' && risTesto!='') {
     12                flagErr=1;
     13             }
     14             else {
     15                flagErr=0;
     16             }
     17             if (numCampo == '3' && risTesto != 'OK!') {
     18                document.getElementById(campo).value ="";
     19             }
     20          }
     21          else {
     22             alert ('Errore HTTP');
     23          }
     24       }
     25      } catch (e) {
     26         alert ('Errore nella funzione di callback: ' + e);
     27        }
     28    }   // fine funzione di callback   
     29    oggXHR.open("post","moduloAjax.php", true);
     30    oggXHR.setRequestHeader("Content-Type",\
  \"application/x-www-form-urlencoded"); 31 var dati = campo + '=' +\
  \encodeURIComponent(document.getElementById(campo).value); 32 if (numCampo == 3) { 33 dati = dati + '&psw=' + document.getElementById('psw').value; 34 } 35 oggXHR.send(dati); 36 } 37 function pulisci(num) { 38 flagErr=0; 39 for (i=1;i<=num;i++) { 40 campoCheck='campoCheck' + i; 41 document.getElementById(campoCheck).innerHTML = ""; 42 } 43 } 44 function inviaDati() { 45 if (flagErr==1) { 46 exit; 47 } 48 oggXHR = new XMLHttpRequest(); 49 oggXHR.onreadystatechange = function() { // funzione di callback 50 try { 51 if (oggXHR.readyState == 4) { 52 if (oggXHR.status == 200) { 53 risTesto = oggXHR.responseText; 54 document.getElementById('risposta').innerHTML = risTesto; 55 } 56 else { 57 alert ('Errore HTTP'); 58 } 59 } 60 } catch (e) { 61 alert ('Errore nella funzione di callback: ' + e); 62 } 63 } // fine funzione di callback 64 oggXHR.open("post","moduloAjaxInvio.php", true); 65 oggXHR.setRequestHeader("Content-Type",\
  \"application/x-www-form-urlencoded"); 66 var dati = 'nomeUtente=' + \
  \encodeURIComponent(document.getElementById('nomeUtente').value); 67 dati = dati + '&psw=' +\
  \encodeURIComponent(document.getElementById('psw').value); 68 dati = dati + '&psw2=' +\
  \encodeURIComponent(document.getElementById('psw2').value); 69 dati = dati + '&indPosta=' +\
  \encodeURIComponent(document.getElementById('indPosta').value); 70 oggXHR.send(dati); 71 }

Servendoci della numerazione delle righe commentiamo i punti più importanti del listato:

Vediamo infine i due listati PHP rispettivamente per il controllo dei singoli campi e per l'elaborazione finale dei dati inviati:

<?php 
$ute['pippo']="pippo@dominio.it";
$ute['pluto']="pluto@dominio.it";
$ute['paperino']="pap@dominio.it";
$uteRev=array_flip($ute);
$nomeUtente=$_POST['nomeUtente'];
$psw=$_POST['psw'];
$psw2=$_POST['psw2'];
$indPosta=$_POST['indPosta'];
if (! empty($nomeUtente)) {
   $ris="OK!";
   if ($ute[$nomeUtente]!="") {
      $ris="Errore: utente esistente";
   }
}
if (! empty($psw)) {
   $ris="OK!";
   if (strlen($psw) < 6) {
      $ris="Errore: password troppo corta";
   }
}
if (! empty($psw2)) {
   $ris="OK!";
   if ($psw != $psw2) {
      $ris="Errore: password diverse";
   }
}
if (! empty($indPosta)) {
   $ris="OK!";
   if (!preg_match("/^[^@ ]{2,}@[^@ \.]+\.[^@ \.]{2,}$/",$indPosta)) {
      $ris="Errore: ind. posta non valido";
   }
   else {
      if ($uteRev[$indPosta]!="") {
         $ris="Errore: ind. mail esistente";
      }
   }
}
print ("$ris");
?>
<?php 
$nomeUtente=$_POST['nomeUtente'];
$psw=$_POST['psw'];
$psw2=$_POST['psw2'];
$indPosta=$_POST['indPosta'];
if (empty($nomeUtente) || empty($psw) || empty($psw2) || empty($indPosta)) {
   $ris="Errore: tutti i campi sono obbligatori!";
}
else {
  // eventuali istruzioni per la memorizzazione dei dati
   $ris="Dati ricevuti!";
}
print ("$ris");
?>

Data la banalità delle elaborazioni svolte, non è necessario fornire grosse spiegazioni sul comportamento dei due script; si noti solo l'uso della funzione array_flip grazie alla quale si ha a disposizione un vettore associativo in cui è scambiato il ruolo dei valori con quello delle chiavi (la cosa è possibile solo se anche i valori, oltre alle chiavi, sono univici) e che viene usato per il controllo di non esistenza dell'indirizzo di posta.

Per il resto sottolineiamo, ancora una volta, le semplificazioni presenti sia nello script dei controlli che in quello per l'invio dai dati, nel quale, tra l'altro, manca del tutto la parte di elaborazione e memorizzazione dei dati ricevuti.

Le due figure 12.19 e 12.20 mostrano sommariamente il funzionamento dell'interfaccia nel caso ci sia un campo errato e in quello in cui l'invio dei dati è andato a buon fine.

Figura 12.19.

figure/pagine-web-ajax-ese0003

Figura 12.20.

figure/pagine-web-ajax-ese0004