Il protocollo HTTP (HyperText Transfer Protocol) insieme al linguaggio di markup HTML costituiscono la base del Web. I dettagli di come richiedere una pagina HTML e di come inviare dati al server sono interamente gestiti dal browser e lo sviluppatore di pagine Web può permettersi di ignorarli.
Questo è vero fino a quando non si hanno esigenze particolari che prevedono l'interazione diretta con il server e la manipolazione di dati da parte del client. Si pensi, ad esempio, all'analisi del contenuto di una pagina Web direttamente tramite il client o alla richiesta di dati caricati dinamicamente sul client o all'invocazione di uno script remoto.
In questi casi la possibilità di utilizzare direttamente il protocollo HTTP risulta molto utile.
Ma come possiamo gestire questo protocollo dall'interno di una pagina Web? Quale supporto viene offerto dai browser?
A queste domande cercheremo di rispondere in questo articolo.
Elementi di HTTP
Il protocollo HTTP si basa su una serie di comandi diretti al server per richiedere o inviare dati. Tra questi, i tre comandi più noti sono:
GET
Tramite questo comando viene richiesta una risorsa sul server. Una risorsa può essere rappresentata da una pagina HTML, un'immagine o un qualsiasi altro tipo di file, ma può essere costituito anche dal risultato dell'esecuzione di uno script o di una pagina ASP, PHP, ecc.
POST
Il comando POST consente di inviare dati al server contestualmente alla richiesta di una risorsa. Generalmente questo comando viene utilizzato per inviare dati raccolti tramite una form.
HEAD
Con questo comando vengono richieste al server informazioni su una risorsa, non la risorsa stessa. Ad esempio, può essere richiesta la data di modifica della risorsa, le dimensioni o il tipo senza caricare la risorsa stessa
Le specifiche del protocollo prevedono altri comandi, come PUT, DELETE, CONNECT, ecc., ma per lo scopo del nostro articolo non ci occuperemo di questi comandi.
HTTP, JavaScript e i browser
Individuati i comandi principali del protocollo HTTP non ci resta che scoprire come fare ad inviarli al server. A parte l'evidente utilizzo implicito dei metodi GET e POST tramite i link e le form HTML, lo strumento chiave per poter effettuare un minimo di elaborazione sui risultati inviati dal server è naturalmente JavaScript.
Ma come possiamo inviare comandi al server utilizzando JavaScript? Purtroppo, mentre il protocollo HTTP rappresenta uno standard di interazione tra un Web server e tutti i browser, la stessa cosa non vale per il supporto offerto dai browser per l'utilizzo del protocollo tramite JavaScript. I principali browser forniscono meccanismi diversi per consentire di interagire con il server dall'interno di una pagina Web.
Il browser di Microsoft consente di sfruttare il protocollo HTTP tramite la libreria MSXML. Per utilizzare la libreria in JavaScript è necessario creare un'istanza della classe XMLHTTP come nel seguente esempio di codice:
objHTTP = new ActiveXObject("MSXML2.XMLHTTP")
Purtroppo, l'identificatore della libreria da passare al costruttore ActiveXObject dipende dalla versione di MSXML installata sulla macchina. L'esempio precedente si riferisce alla versione 3 presente con la versione di Internet Explorer 6.0. Per instanziare la versione precedente, fornita con Internet Explorer 5.0, occorre utilizzare il seguente codice:
objHTTP = new ActiveXObject("Microsoft.XMLHTTP")
Mozilla mette a disposizione l'oggetto XMLHttpRequest equivalente in linea di massima all'oggetto di Microsoft. Tale oggetto è integrato in questo browser e per l'utilizzo con JavaScript è sufficiente una semplice istanziazione come nell'esempio seguente:
objHTTP = new XMLHttpRequest()
L'oggetto XMLHttpRequest è supportato anche da Safari e Opera, anche se con alcune limitazioni e malfunzionamenti.
Un approccio uniforme
Questa situazione non rende agevole scrivere codice JavaScript che sfrutti il protocollo HTTP e che sia indipendente dal browser che lo eseguirà. Perché il protocollo HTTP sia effettivamente utilizzabile dall'interno di pagine Web tramite JavaScript dobbiamo necessariamente rendere uniforme l'approccio offerto dai vari browser.
Confrontando gli oggetti XMLHTTP e XMLHttpRequest ci rendiamo conto che l'oggetto nativo di Mozilla rappresenta la base comune a cui fare riferimento per un supporto uniforme sui più comuni browser.
Per far questo possiamo scrivere una funzione che individua il tipo di supporto offerto dal browser corrente ed restituisce un oggetto per la gestione del protocollo HTTP, come quella che segue.
function getXMLHttp() {
var xmlhttp = null;
if (window.ActiveXObject) {
if (navigator.userAgent.toLowerCase().indexOf("msie 5") != -1)
{
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
} else {
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
}
}
if (!xmlhttp && typeof(XMLHttpRequest) != 'undefined') {
xmlhttp = new XMLHttpRequest()
}
return xmlhttp
}
Questa funzione restituisce un oggetto che corrisponde alla versione di XMLHTTP corretta nel caso di Internet Explorer o all'oggetto di XMLHttpRequest nel caso di Mozilla o degli altri browser che lo prevedono.
Inviare richieste al server...
Dopo aver individuato un metodo per sfruttare il protocollo HTTP indipendentemente dallo specifico browser, vediamo come possiamo inviare richieste al Web server.
Una richiesta diretta al Web server viene effettuata mediante tre passi fondamentali:
- apertura della connessione HTTP
- impostazione della funzione da richiamare al ricevimento della risposta da parte del server
- invio della richiesta
Ad esempio, il seguente codice mostra come richiedere la risorsa test.htm tramite il comando GET:
objHTTP = getXMLHttp();
//Passo 1
objHTTP.open("GET", "test.htm", true);
//Passo 2
objHTTP.onreadystatechange = function() {elaboraRisposta()}
//Passo 3
objHTTP.send(null)
Come si può vedere dall'esempio, tramite il metodo open() apriamo la connessione al server passando il comando HTTP da inviare e la risorsa da richiedere. In questo caso la risorsa richiesta è la pagina test.htm la cui posizione sul server è relativa alla pagina corrente.
Il terzo parametro del metodo open() indica se la modalità di comunicazione con il server deve essere asincrona (true) o sincrona (false). Nel nostro caso abbiamo specificato che la comunicazione deve essere asincrona. Ciò vuol dire che il browser che esegue lo script non rimane in attesa della risposta da parte del server. In caso contrario, il browser rimane in attesa della risposta prima di continuare con l'istruzione successiva e ciò si ripercuote anche sull'interfaccia utente, che non risponde ai comandi, con potenziali conseguenze negative percepite dall'utente.
In generale, sarebbe meglio utilizzare la modalità asincrona di comunicazione.
Se utilizziamo la modalità asincrona, dobbiamo specificare come elaborare la risposta inviata da parte del server. Per questo scopo utilizziamo la proprietà onreadystatechange che ci consente di indicare la funzione che verrà richiamata alla ricezione della risposta da parte del server. Nel nostro esempio abbiamo indicato che alla ricezione della risposta deve essere invocata la funzione elaboraRisposta().
Infine il metodo send() invia l'eventuale corpo della richiesta. In caso di GET e HEAD il corpo della richiesta è vuoto.
...ed analizzare le risposte
Seguendo il nostro esempio, non appena il browser riceve la risposta da parte del server viene eseguita la funzione elaboraRisposta() che ha la seguente forma:
function elaboraRisposta() {
if (objHTTP.readyState == 4) {
alert(objHTTP.responseText)
}
}
La nostra funzione utilizza la proprietà readyState per verificare l'esito della richiesta e la proprietà responseText per visualizzare il contenuto della risposta.
La proprietà readyState può assumere diversi valori in base allo stato della risposta. Il valore 4 indica che il caricamento della risposta da parte del server è stato completato. Nel nostro esempio ci limitiamo a mostrare il contenuto della risposta inviata dal server tramite un alert(), ma è naturale che la risposta può essere visualizzata all'interno della pagina in altri modi oppure può essere elaborata in base al contesto del problema che si sta affrontando.
In alcuni casi si può avere la necessità di accedere agli header HTTP della risposta del server, cioè a quell'insieme di informazioni tecniche inviati dal server come il tipo e la versione del Web server, il tipo di contenuto e le dimensioni in byte della risposta, la data dell'ultima modifica, ecc.
Per accedere a queste informazioni possiamo sfuttare il metodo getAllResponseHeaders(), per ottenere l'insieme di tutti gli header HTTP inviati dal server, ed il metodo getResponseHeader(), per ottenere il valore di un header specifico passato come parametro.
Ad esempio, per ottenere la data dell'ultima modifica del documento inviato come risposta alla nostra richiesta potremmo scrivere:
alert(objHTTP.getResponseHeader("Last-Modified"))
Un altro tipo di informazione che potremmo analizzare in una risposta è il codice di stato HTTP, un'informazione numerica che indica lo stato della risorsa richiesta. Ad esempio, il codice 200 indica che la risorsa richiesta esiste, mentre il codice 404 indica che questa non esiste. Questa informazione è accessibile tramite la proprietà status.
Un'ultima nota
Il supporto del protocollo HTTP tramite JavaScript apre diverse prospettive di utilizzo. Tuttavia occorre tener presente un'importante limitazione.
Normalmente i browser applicano la politica di sicurezza che limita l'accesso allo stesso dominio a cui appartiene la pagina corrente. In pratica ciò vuol dire che non possiamo effettuare richieste ad un server diverso da quello su cui si trova la nostra pagina Web. La limitazione può essere eliminata modificando le politiche di sicurezza dello specifico browser, ma naturalmente questo difficilmente è applicabile in una pagina che fa parte di un sito Web pubblico.
Abbiamo introdotto le nozioni di base per l'utilizzo del protocollo HTTP all'interno di una pagina HTML tramite JavaScript. Proponiamo alcuni esempi di utilizzo del protocollo per illustrarne le potenzialità.
Controllo di link interrotti
Nel corso della vita di un sito Web può capitare che alcuni link inizialmente validi, cioè che puntano a pagine esistenti, possano per cause diverse diventare non più validi o, come spesso si dice, interrotti. Un meccanismo che può risultare utile in queste circostanze è il controllo dei link interrotti tramite una funzione che li individua e ne inibisce la visualizzazione. Ecco una demo.
Per ottenere questo comportamento possiamo scrivere una funzione analoga alla seguente:
function hideLinks() {
var links = document.links
var nLinks = document.links.length
var i
objHTTP = getXMLHttp();
for (i=0;i<nLinks;i++) {
objHTTP.open("HEAD", links[i],false);
objHTTP.send(null);
if (objHTTP.readyState==4) {
if (objHTTP.status==404) links[i].style.visibility = "hidden"
}
}
}
La funzione, dopo avere instanziato l'oggetto objHTTP, effettua un ciclo sull'insieme dei link presenti nel documento corrente e per ciascuno di essi viene eseguito un comando HEAD, per avere informazioni sulla pagina senza caricarla. La richiesta al server viene effettuata in modalità sincrona per consentire di effettuare correttamente il controllo su tutti i link della pagina.
Dopo l'invio della richiesta viene valutata la risposta del server e nel caso in cui la risorsa non esiste, corrispondente al codice di stato 404, il link viene reso invisibile agendo sulla proprietà visibility dello stile del link.
Per l'applicazione effettiva della funzione hideLinks() sulla pagina corrente è sufficiente richiamarla in corrispondenza dell'evento onLoad del body:
<body onLoad="hideLinks()">
Non sempre, però, nascondere un link interrotto ha senso. Si immagini il caso in cui il link si trova all'interno di un testo. Rendere invisibile il link significherebbe rendere illeggibile il testo stesso. In alternativa è possibile eliminare l'URL non valido e visualizzare un tooltip che indica che il link non è più attivo, come nella seguente istruzione:
if (objHTTP.status==404) {
links[i].href = "#";
links[i].title = "Link non più attivo";
}
Esecuzione di script remoti
Un'utile applicazione del protocollo HTTP lato client consiste nell'esecuzione di script remoti. Questo tipo di applicazione consente di richiedere elaborazioni sul server e di presentarle sul client senza necessità di ricaricare la pagina corrente.
Per fare un semplice esempio di questa tecnica, consideriamo la seguente form HTML che prevede l'immissione di due valori numerici e consente di farne la somma (anche qui una semplice demo):
<form>
Valore 1 <input id="v1" type="text"/>
<br/>
Valore 2 <input id="v2" type="text"/>
<br/>
Risultato <input id="risultato" type="text"/>
<br/>
<input type="button" value="Somma" onClick="somma(this.form.v1.value,
this.form.v2.value)"/>
</form>
La funzione JavaScript somma() effettua l'omonima operazione tra i valori passati come argomento invocando una pagina ASP remota somma.asp che resituisce in output semplicemente il risultato della somma. Il seguente codice implementa l'invocazione dello script remoto:
objHTTP = getXMLHttp();
function somma(v1, v2) {
objHTTP.open("GET", " somma.asp?v1=" + v1 + "&v2="
+ v2,true);
objHTTP.onreadystatechange= function() {elaboraRisposta()};
objHTTP.send(null);
}
function elaboraRisposta() {
if (objHTTP.readyState==4) {
document.forms[0].risultato.value = objHTTP.responseText
}
}
Eseguendo lo script l'utente vedrà il risultato nella terza casella senza ricaricare la pagina corrente.
Naturalmente l'esempio ha il solo scopo di presentare il principio dell'invocazione remota di uno script. In casi concreti ha senso invocare uno script remoto soltanto quando l'elaborazione richiede risorse disponibili solo sul server, come ad esempio un database. Ad esempio, si possono immaginare funzioni per il calcolo del codice fiscale, recupero di CAP data una città, ecc.
Recupero di dati da un database
Un altro esempio di invocazione di script remoti è rappresentato dalla richiesta di caricamento di dati da un database. Supponiamo di avere una lista di città e che alla selezione di una città da parte dell'utente vogliamo che vengano visualizzate le relative informazioni descrittive tratte da un database presente sul server. Possiamo recuperare le informazioni descrittive di ciascuna città sfruttando il protocollo HTTP e senza ricaricare la pagina corrente. Ecco la demo.
La lista delle città può essere rappresentata come nel seguente esempio di codice HTML:
<select id="city" onChange="display(this.value)">
<option value="Firenze">Firenze</option>
<option value="Milano">Milano</option>
<option value="Roma">Roma</option>
</select>
La descrizione di ciascuna città viene visualizzata dinamicamente in corrispondenza del cambio della città selezionata eseguendo la funzione JavaScript display():
function display(city) {
objHTTP.open("GET", "infocity.asp?c=" + city,true);
objHTTP.onreadystatechange= function() {elaboraRisposta()};
objHTTP.send(null);
}
function elaboraRisposta() {
if (objHTTP.readyState==4) {
document.getElementById("descrizione").innerHTML = objHTTP.responseText
}
}
La funzione invoca lo script remoto infocity.asp e alla ricezione della risposta ne visualizza il contenuto all'interno di uno specifico <div>.
Conclusioni
Come abbiamo visto, la possibilità di sfruttare il protocollo HTTP tramite JavaScript consente diverse applicazioni pratiche. In particolare, l'esecuzione di script remoti consente di ottimizzare l'uso della banda di trasmissione e di offrire un aggiornamento dinamico della pagina corrente senza ricaricarla interamente.
In questo articolo abbiamo analizzato il supporto di HTTP offerto da XMLHTTP e XMLHttpRequest. Tuttavia ricordiamo che questi oggetti consentono, come suggerisce il nome, anche un supporto per l'elaborazione di documenti XML.