Una delle limitazioni delle pagine Web di qualche anno fa era l'impossibilità di interagire direttamente con il server ed aggiornare i contenuti senza dover ricaricare la pagina.
Le API di comunicazione
Oggi queste limitazioni sono state superate grazie a delle API standard che consentono una certa flessibilità nell'interazione con il server usando JavaScript. In questa sezione vedremo le tre principali API di comunicazione disponibili per una pagina Web per interagire con il server: Ajax
, Server-Sent Events
e WebSocket
.
Ajax
La possibilità di comunicare con il server tramite codice dall'interno di una pagina Web è stato uno dei principali motivi della rinascita di JavaScript. La tecnologia nota come Ajax (Asynchronous JavaScript And XML) rappresenta la rivisitazione di tecnologie esistenti sotto una nuova luce, con l'obiettivo di consentire di inviare e ricevere dati dal server in maniera asincrona e con la possibilità di aggiornare porzioni di una pagina Web.
Anche se l'acronimo fa riferimento a XML come formato di rappresentazione dei dati, in realtà esso non è obbligatorio: infatti una delle rappresentazioni più spesso utilizzate è JSON.
Effettuare una richiesta Ajax
Ajax ruota intorno all'oggetto XMLHttpRequest che rappresenta l'intermediario tra il codice JavaScript eseguito sul browser e il codice eseguito sul server. Questo oggetto mette a disposizione tutti gli strumenti per creare ed inviare richieste HTTP e ricevere ed analizzare le relative risposte.
Quello che segue è un semplice esempio di utilizzo di XMLHttpRequest
:
var httpReq = new XMLHttpRequest();
httpReq.onreadystatechange = function() {
if (httpReq.readyState == 4 && httpReq.status == 200) {
document.getElementById("myDiv").innerHTML = httpReq.responseText;
}
};
httpReq.open("GET", "/myServerPage", true);
httpReq.send();
Analizzando il codice vediamo che, dopo aver creato un'istanza del nostro oggetto, abbiamo associato all'evento readystatechange un gestore che ha il compito di catturare la risposta del server ed assegnarla come contenuto del <div>
myDiv
.
L'evento readystatechange si verifica ad ogni variazione del valore della proprietà readyState
di XMLHttpRequest, che rappresenta lo stato di avanzamento della richiesta. I possibili valori di questa proprietà sono:
Valore | Descrizione |
---|---|
0 |
Richiesta non inizializzata |
1 |
Connessione al server stabilita |
2 |
Ricezione degli header HTTP |
3 |
Ricevimento della risposta |
4 |
Operazione completata |
Nel nostro caso, quando la risposta ricevuta dal server è completa ed il codice di stato HTTP inviato dal server è 200
(valore che indica che non si sono verificati errori), il contenuto inviato dal server e rappresentato dalla proprietà responseText
viene assegnato al <div>
myDiv
.
Open, aprire una richiesta con HTTP GET
Dopo aver preparato la funzione che gestirà l'evento di risposta, apriamo una connessione HTTP con il server tramite il metodo open(). I parametri che passiamo a questo metodo rappresentano:
- il verbo HTTP (
GET
nel nostro caso); - l'URL della pagina o dello script server side richiesto;
- un valore booleano opzionale che indica se la richiesta deve essere effettuata in maniera asincrona (
true
) o sincrona (false
).
Se non è specificato il terzo parametro viene assunto il valore true
e la chiamata sarà asincrona. Infine inviamo la richiesta tramite il metodo send().
Nota: se l'interazione con il server è sincrona, cioè il terzo parametro del metodo open()
è false
, non dobbiamo gestire readystatechange
ma possiamo gestire la risposta del server subito dopo l'invocazione del metodo send()
.
var httpReq = new XMLHttpRequest();
httpReq.open("GET", "/myServerPage", false);
httpReq.send();
if (httpReq.status == 200) {
document.getElementById("myDiv").innerHTML = httpReq.responseText;
}
Gestire la risposta del server
Nell'esempio che abbiamo analizzato abbiamo ignorato la possibilità che il server ci segnali situazioni di errore. In situazioni reali dobbiamo prevedere questa eventualità e gestire opportunamente i codici di stato inviati dal server. Un possibile modo di gestire la situazione è mostrato di seguito:
var httpReq = new XMLHttpRequest();
httpReq.onreadystatechange = function() {
if (httpReq.readyState == 4) {
switch(httpReq.status) {
case 200:
document.getElementById("myDiv").innerHTML = httpReq.responseText;
break;
case 404:
alert("La pagina indicata non esiste!");
break;
case 500:
alert("Si è verificato un errore sul server!");
break;
default:
alert("Non è possibile elaborare la richiesta (" + httpReq.statusText + ")");
}
}
};
Quando la ricezione della risposta è completa viene analizzato il codice di stato ed eseguite le istruzioni corrispondenti. Da notare l'utilizzo della proprietà statusText che rappresenta il testo associato al codice di stato HTTP.
Nel nostro esempio abbiamo utilizzato la proprietà responseText, che restituisce la risposta del server come semplice testo. Se il server invia una risposta in formato XML possiamo utilizzare la proprietà responseXML che restituisce la risposta come documento XML e a cui possiamo applicare i metodi per la relativa manipolazione.
Il seguente esempio mostra come assegnare al <div>
myDiv
il contenuto dell'elemento rappresentato dal tag testo
presente nell'XML inviato dal server:
httpReq.onreadystatechange = function() {
if (httpReq.readyState == 4) {
switch(httpReq.status) {
case 200:
var xmlDoc = httpReq.responseXML;
document.getElementById("myDiv").innerHTML = xmlDoc.getElementByTagName("testo")[0].childNodes[0].nodeValue;
break;
...
}
}
};
La dicitura XML ha motivazioni storiche: infatti anche un documento HTML è disponibile tramite la proprietà responseXML.
Un approccio più generale all'analisi della risposta del server, soprattutto quando il formato dell'informazione che il server ci invia non è determinato a priori, prevede l'utilizzo delle proprietà responseType
e response
. La proprietà responseType
indica il tipo di dato inviato dal server assumendo uno dei seguenti valori:
Campo | Descrizione |
---|---|
text |
La risposta è in formato testuale |
document |
La risposta è un documento XML o HTML |
json |
La risposta è un oggetto JSON risultante dal parsing di una stringa inviata dal server |
arraybuffer |
La risposta è un un buffer di dati binary rappresentati come ArrayBuffer |
blob |
La risposta è un oggetto Blob, assimilabile ad un file |
Una volta determinato il tipo possiamo utilizzare la proprietà response
per accedere al contenuto della risposta:
httpReq.onreadystatechange = function() {
if (httpReq.readyState == 4) {
switch(httpReq.status) {
case 200:
switch(httpReq.responseType) {
case "text":
elaboraTesto(httpReq.response);
break;
case "document":
elaboraDocumento(httpReq.response);
break;
...
}
break;
...
}
}
};
Gestire gli header HTTP
In alcune situazioni può essere necessario inviare al server degli header
HTTP insieme alla richiesta. È ad esempio questo il caso quando si invia una richiesta al server utilizzando il metodo POST, come vedremo più avanti.
In generale, per l'impostazione di un header
abbiamo a disposizione il metodo setRequestHeader()
che prevede due parametri: il nome dell'header
e il suo valore. Il seguente esempio mostra come impostare l'header Accept
per indicare al server in quali formati si preferisce ricevere la risposta:
httpReq.setRequestHeader("Accept", "application/json, text/html, text/*");
httpReq.open("GET", "/myServerPage", true);
httpReq.send();
Nel caso specifico abbiamo indicato che si preferiscono nell'ordine il formato JSON, HTML e qualsiasi formato di tipo testo.
Analogamente è possibile analizzare gli header HTTP inviati dal server con la risposta tramite i metodi getAllResponseHeaders() e getResponseHeader(). Il primo metodo restituisce una stringa con tutti gli header
inviati dal server, mentre il secondo restituisce il valore per un header
specifico.
Il seguente esempio mostra come ottenere le dimensioni in byte della risposta inviata dal server:
var dimensioni = httpReq.getResponseHeader("Content-Length");