AJAX
Il sito web classico
AJAX (Asynchronous Javascript and XML1) è il nome con cui viene chiamata la funzione che usa Javascript nel browser per comunicare con servizi Internet, ed aggiornare quindi i contenuti visualizzati senza ricaricare la pagina.
Per capire il funzionamento di Ajax vediamo prima di tutto il funzionamento di un sito web classico:
- Il browser richiede al server la pagina corrispondente alla URL
- Il server elabora la richiesta e restituisce una pagina HTML e tutti i suoi documenti collegati (CSS, JS, video, audio, ecc.) che insieme costituiscono una risorsa detta statica;
- Il browser riceve la risorsa e la elabora per mostrarla all’utente.
Il server è propriamente un server Web, cioè una applicazione che ha come scopo ricevere richieste da Internet e restituire risposte.

Una volta scaricata la pagina la connessione col server si chiude, e il contenuto viene gestito localmente dal browser. Se si vogliono aggiornamenti sui contenuti (ad esempio ricevere nuove notizie da un sito di informazione, nuovi post da un social, ecc.) bisogna necessariamente ricaricare di nuovo l’intera pagina. Lo stesso vale se si vogliono inviare contenuti (ad esempio un nuovo post, una email, ecc.) Questo significa che:
- l’utente sperimenta una interruzione dell’attività del browser, mentre viene ricaricata la pagina;
- bisogna riscaricare anche risorse che in realtà sono già presenti nella pagina precedenti (css, immagini, ecc.).
Ajax
AJAX (Asynchronous Javascript And XML) è una tecnologia che è stata introdotta per risolvere questo problema. Essa introduce una funzione che consente a Javascript di richiedere contenuti SENZA ricaricare la pagina. Il principio di funzionamento è questo:
- il browser la prima volta scarica la pagina web completa di HTML/CSS/JS ed altre risorse.
- Quando diventa necessario comunicare col server (perchè si richiedono nuovi dati o si devono inviare dati) viene aperta una connessione col server da JS;
- Il server riceve la richiesta e risponde inviando i contenuti richiesti. Javascript gestisce questo come un evento.
Questo meccanismo rende la pagina Web una vera e propria applicazione, che gira interamente all’interno del contesto applicativo del browser e non richiede aggiornamenti di pagina. Questo tipo di applicazione assume il nome di RIA (Rich Internet Application).
Un esempio di RIA è Gmail. L’intera gestione delle mail è gestita da una applicazione Javascript nel browser, che si connette ad Internet solo per inviare e ricevere le mail, come una vera e propria applicazione stand alone. Altri esempi analoghi sono Google Docs, social come Instagram/Facebook/X, o anche applicazioni per la produttività come Canva o Lightroom, applicazioni di streaming video (Netflix, Amazon Prime Video, ecc.) ed infine molti videogiochi (va detto però che alcune di queste applicazioni utilizzano internamente librerie scritte in altri linguaggi, come C++ per migliorare le prestazioni).
Grazie ad AJAX, Javascript è in grado di sostituire la maggior parte delle applicazioni desktop e grazie a questa tecnologia vengono abbattute le barriere che esistono tra un pc, un tablet ed uno smartphone in termini di potenza di calcolo ed operazioni possibili.
Protocollo HTTP
Per poter sviluppare con AJAX occorre comprendere bene il protocollo HTTP. Il protocollo HTTP è un protocollo di comunicazione a livello applicazione dello stack TCP/IP, che sta alla base di tutta la tecnologia del Web.
Questo protocollo prevede una comunicazione tra un client (cioè il browser) ed un server. Ma cosa è il server? Il server è un sistema informatico che fornisce dati e servizi all’interno di una rete, come Internet. E’ sempre attivo, in ascolto, ed accetta connessioni da altri nodi della rete stessa.
Il protocollo prevede che il client crei una richiesta HTTP (Http Request) e il server risponda con una risposta HTTP (Http Response).
Esso ha le seguenti caratteristiche distintive:
- Request e Response sono in formato testo, ma possono contenere dati binari codificati nel corpo della richiesta;
- è una connessione punto-punto: in altre parole solo due sono i nodi (host) coinvolti, il client ed il server;
- il client ha un ruolo attivo: solo il client può originare la richiesta verso il server, e ne attende la risposta.
- Il server è invece in attesa passiva di richieste dal client. Il Web service resta in attesa di richieste e ed elabora le risposte. Non è possibile inviare dati al client senza una richiesta.
- http è senza sessione. Ogni richiesta-risposta è sempre indipendente dalle altre. Non c’è modo, col solo protocollo http, sapere se la richiesta è parte di una sequenza oppure no.
- è opzionalmente previsto uno strato di sicurezza tramite protocollo TLS (che esegue la criptazione dei dati): combinati insieme danno origine al protocollo https.

HTTP Request
Il protcollo prevede una struttura predefinita per la richiesta.
La Request comprende 3 parti:
– una intestazione iniziale, che contiene la URL il protocollo e il metodo della chiamata. Il metodo indica il tipo di rhciesta. Le più comuni sono GET (ricezione dati), POST (invio di nuovi dati), PUT (modifica dei dati), DELETE (cancellazione dati), ma ne sono previsti anche altri.
– l’header della chiamata: contiene delle coppie chiave-valore che contengono informazioni di servizio della chiamata (es. dati autenticazione, formato dei dati della richiesta, cookies, ecc.).
– il body della chiamata: è un campo utilizzato per alcuni metodi (POST-PUT) e contiene i dati inviati al server.
La URL
La URL (Uniform Resource Locator) rappresenta in una stringa di testo che server per identificare in modo univoco le risorse richieste.
La URL identifica il protocollo (http, https), il server (cioè il nome della macchina, comprensiva di dominio e sottodominio), la porta (non obbligatorio indicarla se 80), il path della risorsa (identificata come un filesystem virtuale, non è obbligatorio e dipende dalla configurazione del server) ed eventuali richieste aggiuntive opzionali sulla risorsa stessa (detta query string, non obbligatoria nemmeno questa).
Qui la struttura della stringa:
http[s]://[thirdleveldomain.]secondlevel.firstleveldomain[:port]/[path/to/resource][?ke1=value1][&key2=value2]...
Le parti tra parentesi quadre sono opzionali
Qui qualche esempio di url con tutti gli elementi
http://www.myserver.org/path/to/resource.html?p1=1&p2=ciao
https://www.google.com
https://cipiaceinfo.it/docs/programmazione/javascript/ajax
HTTP Response
Una volta che il server elabora la request, prepara la Response, che contiene 3 parti:
– l’intestazione: essa indica con un codice di status l’esito della risposta (alcuni codici sono 200 OK, 401 non autorizzato, 404 non trovato, 500 errore, ecc.);
– l’header della risposta: contiene delle coppie chiave-valore che contengono informazioni di servizio (es., indica il formato dei dati inviati, regole di sicurezza, codici di autenticazione, cookies, ecc.) in modo analogo a quanto avviene con la richiesta
– il body della risposta con i contenuti inviati.
Web Service e API
Per poter funzionare, lato server viene implementata una speciale applicazione, detta Web Service, che fornisce al Web Server le funzionalità per interagire con l’applicazione client.

Un Web Service è descritto da una WEB API, ovvero un “contratto di utilizzo” che lo descrive tecnicamente, definita da:
- URL
- metodo (GET, POST, ecc.)
- eventuali header richiesti (come l’autenticazione)
- struttura della URL della richiesta
- formato dati richiesta (ad esempio JSON) e sua struttura nel body per le POST/PUT
- formato dati risposta (ad esempio JSON) e sua struttura nel body di risposta
La struttura dei dati della risposta (come anche della richiesta per le richieste con body) sono chiamate tracciato di risposta (o richiesta) e sono un elemento fondamentale per capire come sono strutturati i dati ricevuti (o da inviare).
Le API possono essere descritte in modo formale, con opportuni linguaggi descrittivi.
In queste lezioni useremo principalmente uso di API definite secondo uno standard chiamato REST, che di norma usa il formato dati JSON, nativo per Javascript.
Fetch
JS dispone di una funzione del BOM, chiamata fetch, che svolge le operazioni AJAX, ovvero gestisce richieste e risposte HTTP. Per farlo si appoggia ad una funzione interna del browser, che esegue fisicamente la chiamata e riceve la risposta.
Il meccanismo è descritto in questa figura.

GET
Per fare una richiesta GET occorre eseguire fetch con i parametri della richiesta (URL e talvolta anche alcuni parametri in Header). Qui un esempio:
(async () => {
const response = await fetch("https://dummyjson.com/users/filter?key=firstName&value=Sophia");
const data = await response.json();
console.log(data);
})();L’oggetto response contiene l’intera HTTP response. Nella stragrande maggioranza dei servizi Web, in particolare quelli presenti in queste lezioni, il formato di risposta è JSON. Occorre quindi estrarre dalla risposta il JSON con una seconda operazione await response.json() che estrae il body della risposta e lo converte da JSON ad oggetto Javascript (esegue cioè una JSON.parse())
La stampa del risultato è lasciata per esercizio.
POST
La chiamata POST serve per inviare una richiesta HTTP per inviare dati al server, ad esempio per inserire un nuovo dato inserito dall’utente, per eseguire una login ad un sito, per fare l’upload di immagini o video, per eseguire una ricerca dati complessa, ecc.
La struttura di una chiamata post sarà con la seguente sintassi:
(async () => {
const response = await fetch(url, {
method: "POST",
headers: {
"key": "value",
... // altre chiavi
},
body: content // deve essere una stringa
});
const data = await response.json();
console.log(data);
})();Vediamo un esempio:
(async () => {
const response = await fetch('https://dummyjson.com/users/add', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
firstName: 'Paolo',
lastName: 'Rossi',
age: 25
})
});
const data = await response.json();
console.log(data);
})();Questa fetch esegue una richiesta di invio dati (nel body) al servizio indicato.
Altre funzioni AJAX
PUT
La chiamata PUT serve per inviare una richiesta HTTP per modificare dati al server, ad esempio per modificare un dato presente sul server.
La struttura di una chiamata PUT sarà con la seguente sintassi (molto simile a POST).
(async () => {
const response = await fetch(url, {
method: "PUT",
headers: {
"key": "value",
... // altre chiavi
},
body: content // deve essere una stringa
});
const data = await response.json();
console.log(data);
})();In realtà cambia anche la URL, che va ad identificare la risorsa che si vuole modificare.
(async () => {
const response = await fetch('https://dummyjson.com/users/2', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
age: 26
})
});
const data = await response.json();
console.log(data);
})();Questa fetch esegue una richiesta di invio dati (nel body) al servizio indicato.
DELETE
La chiamata DELETE serve per inviare una richiesta HTTP per eliminare dati dal server.
La struttura di una chiamata DELETE sarà con la seguente sintassi (non è previsto body):
(async () => {
const response = await fetch(url, {
method: "DELETE",
});
const data = await response.json();
console.log(data);
})();La URL di norma identifica la risorsa da eliminare:
(async () => {
const response = await fetch('https://dummyjson.com/users/1', {
method: 'DELETE',
});
const data = await response.json();
console.log(data);
})();Applicazioni AJAX
Struttura generale di una applicazione AJAX
Il modello evento-azione-reazione resta il principio che guida lo sviluppo dell’applicazione, ma siccome una chiamata AJAX richiede del tempo, dobbiamo gestire bene l’attesa della risposta.
E’ importante rivedere bene la struttura generale dell’applicazione:
– tutto il codice è nell’applicazione Javascript, nell’HTML (sebbene sia tecnicamente possibile) non si inserisce mai codice Javascript, per tenere rigidamente separati gli ambiti;
– deve essere previsto un oggetto (o più oggetti) che contiene i dati;
– la render trasforma questo oggetto in html che viene “iniettato” in pagina;
– la gestione di eventi utenti porta ad una azione che agisce solo sui dati ed eventualmente esegue una fetch per comunicare con servizi internet;
– al termine della fetch si va a modificare i dati locale (oppure se serve eseguono nuove fetch (ad esempio per reperire informazioni da altri servizi));
– infine, quando i dati sono pronti per essere visualizzati, viene eseguita una render della sola parte HTML da aggiornare;
Possiamo rappresentare il ciclo di vita dell’applicazione col seguente diagramma:

L’avvio iniziale dell’applicazione porta ad uno stato “pronto” in cui l’applicazione attende eventi utente per cui ha registrato delle azioni. L’azione può comprendere una o più fetch e quindi una modifica dei dati dell’applicazione stessa. Infine, dopo che i dati sono modificati viene eseguita una nuova render, che aggiorna la pagina html con la nuova visualizzazione.
Un esempio reale
Qui sotto un esempio di una semplice applicazione che mostra questo flusso. Si tratta di una applicazione con un html molto semplice che mostra una form con un campo di input, un pulsante ed una tabella con due colonne. L’applicazione riceve in input una parola, la ricerca su un servizio che ne restituisce un JSON contenente il significato, e poi alla tabella viene aggiunta la nuova parola col suo significato.
Il servizio è: https://api.dictionaryapi.dev/api/v2/entries/en/%WORD
Ad esempio con https://api.dictionaryapi.dev/api/v2/entries/en/sea restituisce:
[
{
"word": "sea",
"phonetic": "/siː/",
"phonetics": [
{
"text": "/siː/",
"audio": ""
}
],
"meanings": [
{
"partOfSpeech": "noun",
"definitions": [
{
"definition": "A large body of salt water.",
"synonyms": [
"ogin"
],
"antonyms": []
},
{
"definition": "A lake, especially if large or if salty or brackish.",
"synonyms": [],
"antonyms": [],
"example": "The Caspian Sea, the Sea of Galilee, the Salton Sea, etc."
},
{
"definition": "The swell of the sea; a single wave; billow.",
"synonyms": [],
"antonyms": []
},
{
"definition": "(in combination) Living or used in or on the sea; of, near, or like the sea.",
"synonyms": [],
"antonyms": [],
"example": "Seaman, sea gauge, sea monster, sea horse, sea level, seaworthy, seaport, seaboard, etc."
},
{
"definition": "Anything resembling the vastness or turbulence of the sea.",
"synonyms": [],
"antonyms": []
},
{
"definition": "A constant flux of gluons splitting into quarks, which annihilate to produce further gluons.",
"synonyms": [],
"antonyms": []
},
{
"definition": "A large, dark plain of rock; a mare.",
"synonyms": [],
"antonyms": [],
"example": "The Apollo 11 mission landed in the Sea of Tranquility."
},
{
"definition": "A very large lake of liquid hydrocarbon.",
"synonyms": [],
"antonyms": []
}
],
"synonyms": [
"ogin"
],
"antonyms": []
}
],
"license": {
"name": "CC BY-SA 3.0",
"url": "https://creativecommons.org/licenses/by-sa/3.0"
},
"sourceUrls": [
"https://en.wiktionary.org/wiki/sea"
]
}
]Vogliamo estrarre l’informazione relativa alla prima definizione del testo. Qui lo snippet di codice:
const response = await fetch("https://api.dictionaryapi.dev/api/v2/entries/en/sea");
const data = await response.json();
console.log(data[0].meanings[0].definitions[0].definition]);Vediamo ora il listato completo.
Qui l’HTML:
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.min.js" integrity="sha384-BBtl+eGJRgqQAUMxJ7pMwbEyER4l1g+O15P+16Ep7Q9Q+zqX6gSbd85u4mG4QzX+" crossorigin="anonymous"></script>
</head>
<body class="container">
<div class="row">
<label for="inputWord">Inserisci il valore</label>
<input id="inputWord" type="text">
</div>
<div class="row">
<button type="button" id="submit" >Submit</button>
</div>
<div class="row mt-5">
<table id="tabella" class="table">
</table>
</div>
<script src="index.js" ></script>
</body>
</html>Qui il Javascript:
const inputWord = document.getElementById("inputWord");
const submit = document.getElementById("submit");
const table = document.getElementById("tabella");
const template = "https://api.dictionaryapi.dev/api/v2/entries/en/%WORD";
const htmlTemplate = `<tr><td>%NAME</td><td>%VALUE</td></tr>`;
const header = `<tr><th>Word</th><th>Meaning</th></tr>`;
const list = [];
submit.onclick = async () => {
const word = inputWord.value;
let newUrl = template.replace("%WORD", word);
const response = await fetch(newUrl);
const data = await response.json();
console.log(data);
list.push([word, data[0].meanings[0].definitions[0].definition]);
render();
}
const render = () => {
let html = "";
list.forEach((element) => {
let row = htmlTemplate.replace("%NAME", element[0]);
row = row.replace("%VALUE", element[1]);
html += row;
})
table.innerHTML = header + html;
}[1] Asyncronous Javascript and XML (inizialmente si usava solo con il formato XML).
[2] la conversione è asincrona perché può richiedere un tempo significativo che potrebbe bloccare la richiesta.
[3] questo doppio passaggio della fetch potrebbe essere gestito con una sola promise. Nelle prossime lezioni scriveremo un metodo per gestire tutto con una sola promise.
- Si chiama così perché il formato di interscambio dati era, inizialmente, XML. Oggi il formato prevalente è JSON. ↩︎
