Architettura delle applicazioni web - browser, server web e livelli
In questa pagina 9
Una applicazione web è un programma che gli utenti usano da un browser e che vive in parte sul loro computer, in parte su una o più macchine remote. Questa nota descrive come sono divise le responsabilità (client, server, livelli), che cosa fa un browser quando riceve una pagina e come si è evoluto il web fino a oggi. Le note successive sono sui linguaggi del lato client: Linguaggi di markup - SGML, HTML e XMLUn linguaggio di markup aggiunge al testo marcatori che ne descrivono la struttura. SGML (ISO 8879, 1986) è il metalinguaggio da cui derivano HTML fino alla 4.01 e XML 1.0 (W3C, 1998, un sottoinsieme semplificato); HTML5 ha un proprio algoritmo di parsing, tollerante, e non è né SGML né XML. Un documento XML è ben formato se rispetta la sintassi (un'unica radice, tag annidati e chiusi, attributi tra virgolette, nomi con maiuscole e minuscole distinte, entità per i caratteri speciali) ed è valido se in più rispetta uno schema. La DTD dichiara elementi (ELEMENT: EMPTY, ANY, #PCDATA, sequenza con la virgola, scelta con |, cardinalità ? * +) e attributi (ATTLIST: CDATA, ID, IDREF, enumerazioni; #REQUIRED, #IMPLIED, #FIXED); non ha tipi di dato e non conosce i namespace. XML Schema (XSD) è scritto in XML e ha tipi (xs:date, xs:positiveInteger), restrizioni con pattern, minOccurs e maxOccurs, key e keyref. I namespace (xmlns, xmlns:prefisso) identificano il vocabolario con un URI; gli attributi senza prefisso non appartengono a nessun namespace. XML è rigido e adatto a documenti e configurazioni (web.xml, pom.xml), HTML è tollerante e ha un vocabolario fisso; JSON è l'alternativa più compatta per scambiare dati e non ha attributi, schema standard o contenuto misto.Linguaggi di markup - SGML, HTML e XML →, HTML5 - struttura del documento, elementi semantici e formUn documento HTML5 ha la forma doctype (), html con lang, head (meta charset UTF-8, viewport, title, link, script) e body. Il parser HTML5 non dà mai errore: corregge (aggiunge il tbody, chiude i p). Gli elementi semantici (header, nav, main, section, article, aside, footer, figure, time) descrivono il ruolo del contenuto e danno ai lettori di schermo i punti di riferimento; div e span sono neutri. Link: a href (assoluto, relativo, #frammento, mailto), rel e target; immagini con alt obbligatorio. Tabelle: table, caption, thead, tbody, th con scope, td, colspan e rowspan, solo per dati. Form: action, method (GET mette i campi nella query string, per letture idempotenti e ricercabili; POST li mette nel corpo, per modificare lo stato), enctype (application/x-www-form-urlencoded predefinito, multipart/form-data per i file); si invia solo ciò che ha name; label for collega etichetta e campo; tipi di input (text, email, number, date, time, radio, checkbox, hidden...); validazione nativa con required, minlength, maxlength, min, max, step, pattern e type, aggirabile e quindi da ripetere sul server; novalidate la spegne. Accessibilità: lang, alt, label, gerarchia dei titoli, navigazione da tastiera, ARIA solo quando l'HTML nativo non basta.HTML5 - struttura del documento, elementi semantici e form →, CSS3 - selettori, cascata, box model e layoutCSS descrive la presentazione: regole selettore { proprietà: valore }. Selettori: tipo, classe (.a), id (#a), attributo ([type="date"]), universale, combinatori (spazio discendente, > figlio, + fratello adiacente, ~ fratelli seguenti), pseudo-classi (:hover, :focus-visible, :nth-child, :not, :checked) e pseudo-elementi (::before, ::after). Cascata: tra più regole che toccano la stessa proprietà vince, nell'ordine, l'importanza (!important vince, e lo stile inline batte gli stili normali), poi la specificità (a,b,c) = (id, classi+attributi+pseudo-classi, tipi+pseudo-elementi) confrontata come numeri uno alla volta da sinistra, infine l'ordine di comparsa (l'ultima vince). Ereditarietà: color, font, line-height, text-align passano ai figli; margin, padding, border, background, width no; inherit, initial, unset la controllano. Box model: content, padding, border, margin; con content-box width è solo il contenuto, con border-box include padding e bordo; i margini verticali adiacenti collassano nel maggiore. display: block, inline, inline-block, none, flex, grid. position: static, relative, absolute (rispetto all'antenato posizionato), fixed, sticky. Flexbox è per una dimensione (justify-content, align-items, gap, flex-grow, flex-wrap), grid per due (grid-template-columns, fr, repeat, minmax, gap). Responsive: meta viewport, unità relative (rem, %, fr), media query @media (min-width), approccio mobile-first.CSS3 - selettori, cascata, box model e layout → e JavaScript - linguaggio, DOM ed eventiJavaScript (standard ECMAScript, Ecma-262) è il linguaggio dei browser: tipizzato dinamicamente, con sette tipi primitivi (number, string, boolean, undefined, null, bigint, symbol) e gli oggetti (anche array e funzioni); typeof null vale "object" per un errore storico; == converte i tipi, === no (si usa sempre ===); valori falsy: false, 0, 0n, "", null, undefined, NaN. let e const hanno visibilità di blocco e una zona morta prima della dichiarazione, var visibilità di funzione e hoisting; const impedisce di riassegnare il nome, non di modificare l'oggetto. Funzioni: dichiarazione (hoisted), espressione, arrow function (this lessicale), parametri predefiniti, rest e spread, destructuring. Array: map, filter, reduce, find, some, every, sort (senza comparatore ordina come stringhe). Closure: una funzione ricorda le variabili dell'ambiente in cui è nata. Eventi: addEventListener; propagazione in tre fasi (cattura, target, bolla), stopPropagation, preventDefault, delegazione con event.target e closest. DOM: albero di nodi; querySelector, createElement, append, replaceChildren, textContent (sicuro) contro innerHTML (rischio di XSS), classList, dataset. Moduli ES (type="module", import e export) sono differiti e in modalità strict. Event loop: un solo thread, call stack, coda dei task (setTimeout, eventi) e coda dei microtask (promise, await) svuotata dopo ogni task: l'esecuzione asincrona (fetch, async e await, Promise.all) è alla base di AJAX e il codice lungo blocca la pagina.JavaScript - linguaggio, DOM ed eventi →.
Il protocollo di trasporto, cioè HTTP, è già trattato in HTTP 0.9 e 1.0 - struttura dei messaggi, header e parsingHTTP/0.9 (1991) ha una sola riga di richiesta "GET /percorso" e la risposta e' solo il documento HTML, senza codici di stato ne' header, e la connessione si chiude alla fine; HTTP/1.0 (RFC 1945) aggiunge Request-Line con versione, Status-Line con codice, header (generali, di richiesta, di risposta, di entita'), i metodi HEAD e POST, tipi di contenuto diversi dall'HTML; un messaggio e' riga iniziale, header "Nome: valore" a righe CRLF, riga vuota, corpo; in HTTP/1.0 il corpo della risposta finisce quando il server chiude la connessione, mentre per POST serve Content-Length; il parsing legge la prima riga, poi gli header fino alla riga vuota, poi il corpo; i nomi degli header non distinguono maiuscole e minuscole, e un client robusto accetta LF semplice e spazi variabili.HTTP 0.9 e 1.0 - struttura dei messaggi, header e parsing →, HTTP 1.1 - connessioni persistenti, Content-Length e chunked transfer encodingHTTP/1.1 (oggi RFC 9110 e 9112) rende la connessione persistente di default (si chiude solo con "Connection: close"), rende obbligatorio l'header Host (virtual hosting) e introduce i nuovi metodi PUT, DELETE, OPTIONS, TRACE, Expect: 100-continue, richieste di intervalli (206) e Transfer-Encoding: chunked; con la connessione persistente il client deve sapere dove finisce ogni risposta: lunghezza del corpo nell'ordine HEAD/1xx/204/304 senza corpo, Transfer-Encoding chunked, Content-Length, altrimenti fino alla chiusura; il chunked divide il corpo in blocchi preceduti dalla lunghezza in esadecimale, termina con un chunk 0 e un trailer facoltativo, e si decodifica contando i byte dichiarati (non cercando CRLF).HTTP 1.1 - connessioni persistenti, Content-Length e chunked transfer encoding → e Caching, autenticazione, tipi MIME e URI in HTTPLa cache HTTP riusa le risposte senza contattare il server finche' sono fresche (Cache-Control: max-age, Expires; in mancanza una durata euristica pari a circa il 10% del tempo trascorso da Last-Modified) e, quando sono scadute, le rivalida con una richiesta condizionale (If-None-Match con ETag, If-Modified-Since con Last-Modified) alla quale il server risponde 304 senza corpo; no-store vieta di memorizzare, no-cache obbliga a rivalidare, private esclude le cache condivise. L'autenticazione usa 401 con WWW-Authenticate e la risposta Authorization: Basic (base64 di utente:password, solo sopra TLS) o Digest (hash con nonce); 407 vale per i proxy. Content-Type porta il tipo MIME (tipo/sottotipo; parametri come charset e boundary), Accept e gli altri header Accept-* guidano la negoziazione. Un URI (RFC 3986) e' scheme://userinfo@host:porta/percorso?query#frammento, con percent-encoding %HH per i byte non ammessi e regole per risolvere i riferimenti relativi.Caching, autenticazione, tipi MIME e URI in HTTP →. Qui lo si richiama soltanto.
L'esempio che useremo
In tutta la parte di tecnologie web si sviluppa una piccola applicazione di prenotazione delle aule. Il database ha due tabelle (schema e vincoli come in SQL - definizione dei dati e vincoliSQL (standard ISO 9075) è un linguaggio dichiarativo con una parte di definizione dei dati (DDL: CREATE/DROP/ALTER di database, schemi, tipi enumerati, domini, tabelle) e una di manipolazione (DML: INSERT, DELETE, UPDATE, SELECT). Una tabella SQL è un multi-insieme di righe (può avere duplicati), quindi non è una relazione in senso stretto. CREATE TABLE elenca colonne con tipo e vincoli: NOT NULL, DEFAULT, CHECK (di colonna o di tupla), PRIMARY KEY (una sola, anche composta), UNIQUE, FOREIGN KEY ... REFERENCES con ON DELETE/ON UPDATE (NO ACTION, CASCADE, SET NULL, SET DEFAULT). I tipi principali sono CHAR/VARCHAR, NUMERIC, INTEGER, SERIAL, BOOLEAN, DATE, TIME, TIMESTAMP, più UUID, intervalli e JSON in PostgreSQL. DROP ... CASCADE cancella anche gli oggetti dipendenti, RESTRICT (predefinito) rifiuta se ce ne sono. DELETE e UPDATE senza WHERE agiscono su tutta la tabella.SQL - definizione dei dati e vincoli → e Modello relazionale - relazioni, chiavi e vincoliIl modello relazionale (Codd, IBM, 1970) rappresenta i dati con relazioni, cioè tabelle, ed è basato su valori: i legami tra relazioni sono confronti di valori, non puntatori, il che garantisce indipendenza dalle strutture fisiche, portabilità e bidirezionalità. Una relazione matematica è un sottoinsieme del prodotto cartesiano di n domini (insieme di n-uple ordinate, senza duplicati né ordine tra le righe); una relazione del modello è un insieme di tuple, funzioni dagli attributi ai valori del dominio, quindi non posizionale. Si distinguono schema di relazione e istanza, schema di base di dati (relazioni più vincoli) e istanza. NULL è un valore speciale fuori da ogni dominio. I vincoli di integrità sono intra-relazionali (di dominio, di tupla, di chiave) e inter-relazionali (integrità referenziale, con la chiave esterna). Superchiave: insieme di attributi che non ripete mai i valori; chiave: superchiave minimale; ogni relazione ne ha almeno una (tutti gli attributi); la chiave primaria è una chiave scelta, senza NULL.Modello relazionale - relazioni, chiavi e vincoli →):
CREATE TABLE aula (
id INTEGER PRIMARY KEY,
nome VARCHAR(40) NOT NULL UNIQUE,
posti INTEGER NOT NULL CHECK (posti > 0)
);
CREATE TABLE prenotazione (
id INTEGER PRIMARY KEY,
aula_id INTEGER NOT NULL REFERENCES aula(id),
giorno DATE NOT NULL,
ora_inizio TIME NOT NULL,
ora_fine TIME NOT NULL,
studente VARCHAR(40) NOT NULL,
CHECK (ora_fine > ora_inizio)
);I dati di esempio, che si ritrovano in tutte le note e gli esercizi:
| aula.id | nome | posti |
|---|---|---|
| 1 | Aula A1 | 80 |
| 2 | Aula B2 | 45 |
| 3 | Lab Info | 30 |
| prenotazione.id | aula_id | giorno | ora_inizio | ora_fine | studente |
|---|---|---|---|---|---|
| 1 | 1 | 2026-10-19 | 09:00 | 11:00 | Rossi |
| 2 | 1 | 2026-10-19 | 14:00 | 16:00 | Bianchi |
| 3 | 2 | 2026-10-20 | 10:00 | 12:00 | Verdi |
| 4 | 3 | 2026-10-21 | 09:00 | 13:00 | Rossi |
Il server espone i dati come JSON all'indirizzo /api/aule e /api/prenotazioni (con la lista delle prenotazioni di un giorno: /api/prenotazioni?giorno=2026-10-19); una pagina HTML con JavaScript li mostra e permette di prenotare. In JSON i nomi dei campi sono quelli delle colonne (aula_id, ora_inizio, ora_fine).
Client e server
Il modello di base è client-server: due programmi comunicano in rete, il client (client) apre la conversazione e chiede qualcosa, il server (server) resta in ascolto e risponde. Sul web:
- il client è il browser (web browser): Firefox, Chrome, Safari, Edge;
- il server è un programma in ascolto di solito sulla porta 80 (HTTP) o 443 (HTTPS);
- si parlano in HTTP: il client manda una richiesta (metodo, indirizzo, intestazioni, eventuale corpo), il server manda una risposta (codice di stato, intestazioni, corpo).
Esempio. Il browser chiede le prenotazioni del 19 ottobre e il server risponde con un array JSON (207 byte di corpo):
GET /api/prenotazioni?giorno=2026-10-19 HTTP/1.1 Host: aule.example.org Accept: application/json HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 Content-Length: 207 [{"id":1,"aula_id":1,"giorno":"2026-10-19","ora_inizio":"09:00","ora_fine":"11:00","studente":"Rossi"},{"id":2,"aula_id":1,"giorno":"2026-10-19","ora_inizio":"14:00","ora_fine":"16:00","studente":"Bianchi"}]La riga vuota separa le intestazioni dal corpo;
Content-Lengthconta i byte del corpo (HTTP 1.1 - connessioni persistenti, Content-Length e chunked transfer encodingHTTP/1.1 (oggi RFC 9110 e 9112) rende la connessione persistente di default (si chiude solo con "Connection: close"), rende obbligatorio l'header Host (virtual hosting) e introduce i nuovi metodi PUT, DELETE, OPTIONS, TRACE, Expect: 100-continue, richieste di intervalli (206) e Transfer-Encoding: chunked; con la connessione persistente il client deve sapere dove finisce ogni risposta: lunghezza del corpo nell'ordine HEAD/1xx/204/304 senza corpo, Transfer-Encoding chunked, Content-Length, altrimenti fino alla chiusura; il chunked divide il corpo in blocchi preceduti dalla lunghezza in esadecimale, termina con un chunk 0 e un trailer facoltativo, e si decodifica contando i byte dichiarati (non cercando CRLF).HTTP 1.1 - connessioni persistenti, Content-Length e chunked transfer encoding →). Le due metà sono in realtà messaggi separati: richiesta del client, poi risposta del server.
Due conseguenze importanti di HTTP (RFC 9110):
- È senza stato (stateless): ogni richiesta è indipendente, il server non si ricorda delle precedenti. Per sapere "chi è l'utente" il server assegna un identificativo di sessione (session) che il browser rimanda a ogni richiesta in un cookie (RFC 6265). Lo stato (utente, carrello) sta sul server e il cookie ne è la chiave.
- È il client a prendere l'iniziativa. Il server non può "spingere" dati al browser di sua iniziativa con HTTP semplice; per notifiche in tempo reale esistono altri meccanismi (WebSocket, QUIC e HTTP-3HTTP e' richiesta e risposta, quindi poco adatto a notifiche e dati in tempo reale (polling, long polling, SSE); WebSocket (RFC 6455) apre con un handshake HTTP/1.1 Upgrade (Sec-WebSocket-Key a 16 byte casuali in Base64, risposta 101 con Sec-WebSocket-Accept = Base64 di SHA-1 di chiave piu' GUID fisso) un canale bidirezionale persistente sulla stessa connessione TCP, con frame di 2-14 byte di intestazione (FIN, opcode, MASK, lunghezza su 7, 16 o 64 bit, chiave di mascheramento obbligatoria dal client al server) e messaggi di testo, binari, close, ping e pong. QUIC (RFC 9000) e' un protocollo di trasporto sopra UDP con TLS 1.3 integrato, flussi indipendenti (niente head-of-line blocking fra flussi), connection ID che permettono di cambiare rete, handshake in 1 RTT e ripresa in 0 RTT; HTTP/3 (RFC 9114) mappa HTTP su QUIC con un flusso per richiesta e QPACK per gli header, ed e' annunciato con Alt-Svc.WebSocket, QUIC e HTTP-3 →).
Architettura a livelli
| livello | compito | nell'esempio |
|---|---|---|
| presentazione (presentation) | mostrare i dati, raccogliere input, reagire ai clic | pagina HTML, CSS e JavaScript nel browser |
| logica applicativa (business logic) | regole: "un'aula non si prenota due volte nella stessa ora", autenticazione, formato delle risposte | servlet Java (AuleServlet, PrenotazioniServlet) sul server |
| dati (data) | memorizzare e interrogare in modo persistente | database con le tabelle aula e prenotazione, accessibili con AulaDAO e PrenotazioneDAO |
Ogni livello parla solo con quelli adiacenti: il browser non apre mai una connessione al database; il database non sa nulla di HTML. I vantaggi:
- si può cambiare un livello senza toccare gli altri (nuovo database, nuova grafica, un'app per telefono che riusa la stessa logica);
- si scala per livello: più copie del server applicativo davanti allo stesso database;
- la sicurezza si concentra nella logica: il browser è fuori dal controllo dell'applicazione (l'utente può modificare qualsiasi cosa), quindi ogni controllo fatto in JavaScript va rifatto sul server. La validazione dei form nel browser serve solo per essere comodi.
Livello (tier) e strato (layer). Il livello è una divisione fisica: macchine o processi distinti. Lo strato è una divisione logica del codice (per esempio, dentro l'applicazione Java: servlet, classi di dominio, classi di accesso ai dati). Un'applicazione a tre livelli ha tipicamente più strati nel livello centrale, e la divisione in strati secondo il paradigma MVC (Model-View-Controller) è un tema della parte lato server.
Confronto con le alternative:
- a due livelli (two-tier): il client parla direttamente con il database (le vecchie applicazioni desktop che usavano JDBC). La logica sta nel client, che va installato e aggiornato su ogni PC, e le credenziali del database sono esposte;
- a tre o più livelli (n-tier): si aggiungono per esempio una cache, un servizio di autenticazione, altri servizi interni.
Un client sottile (thin client) lascia al server quasi tutta la logica e riceve pagine già pronte (il web classico); un client spesso (thick o rich client) esegue molta logica in JavaScript e scambia solo dati (applicazioni a pagina singola, single-page application).
Server web e application server
Sul lato server ci sono due ruoli che nei sistemi reali si incontrano nello stesso prodotto o in prodotti diversi:
- il server web (web server; Apache httpd, nginx) gestisce HTTP: apre le connessioni, legge le richieste, serve i file statici (HTML, CSS, immagini, JavaScript) dal disco, applica cache, compressione, HTTPS;
- l'application server esegue il codice dell'applicazione. Per Java, il contenitore di servlet (servlet container, per esempio Apache Tomcat) carica le classi, crea un oggetto servlet per ogni indirizzo configurato, gli passa richieste e risposte, gestisce le sessioni e il ciclo di vita (CGI e applicazioni web dinamicheLa Common Gateway Interface (CGI, RFC 3875) e' l'interfaccia fra un server web e un programma esterno che genera la risposta: per ogni richiesta il server fa fork ed exec del programma, gli passa i dati con le meta-variabili d'ambiente (REQUEST_METHOD, QUERY_STRING, CONTENT_LENGTH, CONTENT_TYPE, SCRIPT_NAME, PATH_INFO, SERVER_*, REMOTE_ADDR, HTTP_* per gli header) e con lo standard input (corpo della POST, CONTENT_LENGTH byte); il programma scrive sullo standard output header CGI (Content-Type obbligatorio, Status, Location), una riga vuota e il corpo, e il server li trasforma in una risposta HTTP completa; i punti delicati sono pipe e dup2, la chiusura delle estremita' inutilizzate, il timeout, i limiti di dimensione e la sicurezza (injection, path traversal, header Proxy, shellshock); il costo di un processo per richiesta ha portato a FastCGI, ai moduli del server e ai server applicativi.CGI e applicazioni web dinamiche → racconta la stessa idea con programmi esterni lanciati a ogni richiesta; il container la sostituisce con codice già in memoria).
Tomcat può fare anche da server web (serve i file statici di una applicazione), ma in produzione spesso c'è un reverse proxy davanti: nginx o Apache ricevono le richieste dal browser, rispondono direttamente per i file statici e inoltrano all'application server solo quelle dinamiche (/api/...) (DNS, proxy web, HTTP CONNECT e gateway applicativiUn programma risolve i nomi con getaddrinfo (file hosts e poi DNS); un messaggio DNS (RFC 1035) ha un header di 12 byte (ID, flag QR/RD/RA/TC e RCODE, quattro contatori), una domanda (QNAME a etichette con lunghezza, QTYPE, QCLASS) e record di risposta con nomi eventualmente compressi da puntatori, su UDP porta 53 con ripiego su TCP se il bit TC e' acceso; un web proxy e' un intermediario scelto dal client che riceve richieste con URI assoluto, le inoltra al server (togliendo gli header hop-by-hop, aggiungendo Via e X-Forwarded-For) e puo' memorizzare le risposte; le cache si organizzano in gerarchie e con funzioni hash coerenti, le CDN le replicano vicino agli utenti; il metodo CONNECT chiede al proxy un tunnel TCP verso host:porta e dopo la risposta 2xx il proxy inoltra i byte in entrambe le direzioni senza interpretarli (HTTPS, con restrizione delle porte); un gateway o reverse proxy e' un intermediario scelto dal server che traduce o inoltra le richieste ad altri sistemi (bilanciamento, terminazione TLS, FastCGI, API gateway).DNS, proxy web, HTTP CONNECT e gateway applicativi →). Si ottengono HTTPS in un solo punto, compressione, bilanciamento del carico fra più istanze.
Pagine statiche e dinamiche.
| statica | dinamica | |
|---|---|---|
| contenuto | file che esiste così com'è sul server | prodotto al momento della richiesta |
| esempio | /stile.css, /index.html |
elenco delle prenotazioni di oggi |
| chi la produce | il web server | un programma: CGI, servlet, JSP, PHP... |
| varia con | niente (stesso file per tutti) | parametri della richiesta, utente, ora, contenuto del database |
Anche una pagina "statica" può diventare dinamica nel browser: l'HTML è sempre lo stesso, ma JavaScript lo modifica dopo il caricamento (per esempio costruisce la tabella delle prenotazioni da un array JSON). Quindi "dinamico" può accadere sul server, sul client o in entrambi: è la differenza fra Web 1.0 e Web 2.0 più sotto.
Che cosa fa un browser
Un browser contiene sei componenti principali: l'interfaccia (barra degli indirizzi, schede), il motore di rete (HTTP, cache, cookie), il motore di rendering (rendering engine: parsing e disegno), l'interprete JavaScript, il gestore di memorizzazione (cookie, localStorage) e il motore grafico. Quando si digita un indirizzo:
- Rete. Risoluzione DNS, connessione TCP e TLS, richiesta
GET, risposta conContent-Type: text/html(DNS, proxy web, HTTP CONNECT e gateway applicativiUn programma risolve i nomi con getaddrinfo (file hosts e poi DNS); un messaggio DNS (RFC 1035) ha un header di 12 byte (ID, flag QR/RD/RA/TC e RCODE, quattro contatori), una domanda (QNAME a etichette con lunghezza, QTYPE, QCLASS) e record di risposta con nomi eventualmente compressi da puntatori, su UDP porta 53 con ripiego su TCP se il bit TC e' acceso; un web proxy e' un intermediario scelto dal client che riceve richieste con URI assoluto, le inoltra al server (togliendo gli header hop-by-hop, aggiungendo Via e X-Forwarded-For) e puo' memorizzare le risposte; le cache si organizzano in gerarchie e con funzioni hash coerenti, le CDN le replicano vicino agli utenti; il metodo CONNECT chiede al proxy un tunnel TCP verso host:porta e dopo la risposta 2xx il proxy inoltra i byte in entrambe le direzioni senza interpretarli (HTTPS, con restrizione delle porte); un gateway o reverse proxy e' un intermediario scelto dal server che traduce o inoltra le richieste ad altri sistemi (bilanciamento, terminazione TLS, FastCGI, API gateway).DNS, proxy web, HTTP CONNECT e gateway applicativi →). - Decodifica e tokenizzazione. I byte diventano caratteri (con la codifica dichiarata,
UTF-8), poi il tokenizzatore HTML li trasforma in token: tag di apertura, tag di chiusura, testo, commenti. - Costruzione del DOM. Dai token si costruisce il DOM (Document Object Model), un albero in memoria dove ogni tag diventa un nodo elemento e il testo un nodo testo. L'algoritmo è definito da WHATWG HTML e non fallisce mai: davanti a un HTML sbagliato corregge (chiude i tag mancanti, mette il
<tbody>che manca) invece di dare errore. Per questo il DOM può differire dal sorgente. - CSSOM. Ogni foglio di stile (
<link rel="stylesheet">,<style>) viene analizzato in un albero di regole, il CSSOM. Per ogni nodo del DOM si calcola lo stile finale applicando la cascata (CSS3 - selettori, cascata, box model e layoutCSS descrive la presentazione: regole selettore { proprietà: valore }. Selettori: tipo, classe (.a), id (#a), attributo ([type="date"]), universale, combinatori (spazio discendente, > figlio, + fratello adiacente, ~ fratelli seguenti), pseudo-classi (:hover, :focus-visible, :nth-child, :not, :checked) e pseudo-elementi (::before, ::after). Cascata: tra più regole che toccano la stessa proprietà vince, nell'ordine, l'importanza (!important vince, e lo stile inline batte gli stili normali), poi la specificità (a,b,c) = (id, classi+attributi+pseudo-classi, tipi+pseudo-elementi) confrontata come numeri uno alla volta da sinistra, infine l'ordine di comparsa (l'ultima vince). Ereditarietà: color, font, line-height, text-align passano ai figli; margin, padding, border, background, width no; inherit, initial, unset la controllano. Box model: content, padding, border, margin; con content-box width è solo il contenuto, con border-box include padding e bordo; i margini verticali adiacenti collassano nel maggiore. display: block, inline, inline-block, none, flex, grid. position: static, relative, absolute (rispetto all'antenato posizionato), fixed, sticky. Flexbox è per una dimensione (justify-content, align-items, gap, flex-grow, flex-wrap), grid per due (grid-template-columns, fr, repeat, minmax, gap). Responsive: meta viewport, unità relative (rem, %, fr), media query @media (min-width), approccio mobile-first.CSS3 - selettori, cascata, box model e layout →). - Render tree. Unendo DOM e CSSOM si ottiene l'albero di rendering, che contiene solo i nodi visibili (un
<head>o un elemento condisplay: nonenon ci sono) con il loro stile. - Layout. Per ogni nodo si calcolano posizione e dimensione in pixel, a partire dalla larghezza della finestra. Se la finestra cambia o un elemento cambia misura, il layout si ricalcola (reflow).
- Paint e composizione. Si dipingono i pixel (testo, colori, bordi, immagini) in strati che la scheda grafica compone sullo schermo. Un cambio di solo colore richiede solo il repaint, senza nuovo layout.
Esempio: il documento
<!DOCTYPE html>
<html lang="it">
<head><title>Aule</title></head>
<body>
<h1>Aule</h1>
<ul><li>Aula A1</li><li>Lab Info</li></ul>
</body>
</html>dà questo DOM (rientro = figlio):
Document
└─ html (lang="it")
├─ head
│ └─ title
│ └─ "Aule"
└─ body
├─ h1
│ └─ "Aule"
└─ ul
├─ li ─ "Aula A1"
└─ li ─ "Lab Info"Gli spazi e i ritorni a capo fra i tag diventano a loro volta nodi di testo (qui omessi). Ogni nodo ha un padre e dei figli: è questa struttura che JavaScript naviga e modifica (JavaScript - linguaggio, DOM ed eventiJavaScript (standard ECMAScript, Ecma-262) è il linguaggio dei browser: tipizzato dinamicamente, con sette tipi primitivi (number, string, boolean, undefined, null, bigint, symbol) e gli oggetti (anche array e funzioni); typeof null vale "object" per un errore storico; == converte i tipi, === no (si usa sempre ===); valori falsy: false, 0, 0n, "", null, undefined, NaN. let e const hanno visibilità di blocco e una zona morta prima della dichiarazione, var visibilità di funzione e hoisting; const impedisce di riassegnare il nome, non di modificare l'oggetto. Funzioni: dichiarazione (hoisted), espressione, arrow function (this lessicale), parametri predefiniti, rest e spread, destructuring. Array: map, filter, reduce, find, some, every, sort (senza comparatore ordina come stringhe). Closure: una funzione ricorda le variabili dell'ambiente in cui è nata. Eventi: addEventListener; propagazione in tre fasi (cattura, target, bolla), stopPropagation, preventDefault, delegazione con event.target e closest. DOM: albero di nodi; querySelector, createElement, append, replaceChildren, textContent (sicuro) contro innerHTML (rischio di XSS), classList, dataset. Moduli ES (type="module", import e export) sono differiti e in modalità strict. Event loop: un solo thread, call stack, coda dei task (setTimeout, eventi) e coda dei microtask (promise, await) svuotata dopo ogni task: l'esecuzione asincrona (fetch, async e await, Promise.all) è alla base di AJAX e il codice lungo blocca la pagina.JavaScript - linguaggio, DOM ed eventi →).
Script, CSS e blocco del rendering
Il parser HTML lavora mentre i byte arrivano, ma due cose lo rallentano:
- un foglio di stile blocca il rendering (il browser non disegna finché non l'ha caricato, per non mostrare una pagina senza stile) e anche l'esecuzione degli script che seguono, che potrebbero leggere gli stili;
- uno script classico
<script src="app.js"></script>blocca il parsing: il parser si ferma, scarica ed esegue lo script, poi riprende, perché lo script può modificare il documento condocument.write.
Gli attributi che cambiano questo comportamento:
| attributo | scarica | esegue |
|---|---|---|
| nessuno | fermando il parser | subito, fermando il parser |
defer |
in parallelo al parsing | dopo la fine del parsing, nell'ordine in cui compaiono, prima di DOMContentLoaded |
async |
in parallelo | appena scaricato, in ordine qualsiasi |
type="module" |
in parallelo | come defer |
Regola pratica: gli script che toccano il DOM vanno con defer nell'<head> o in fondo al <body>. L'evento DOMContentLoaded scatta quando il DOM è completo (immagini escluse); load quando è arrivato tutto, immagini comprese.
Origine e same-origin policy
Un'origine (origin) è la terna schema + host + porta: https://aule.example.org e https://aule.example.org:8443 sono origini diverse, come http:// e https://. La same-origin policy impedisce a uno script caricato da un'origine di leggere le risposte di un'altra: una pagina qualsiasi non può leggere i dati che l'utente ha su un altro sito. Un server può autorizzare esplicitamente altre origini con le intestazioni CORS (cross-origin resource sharing). Il caso tipico dell'esempio: pagina e API sono servite dalla stessa origine, e la policy non dà problemi.
Web 1.0, 2.0 e 3.0
| Web 1.0 | Web 2.0 | Web 3.0 (web semantico) | |
|---|---|---|---|
| idea | pubblicare documenti | applicazioni e contenuti degli utenti | dati con un significato leggibile dalle macchine |
| lato client | HTML (quasi) senza script | JavaScript, AJAX, CSS, applicazioni a pagina singola | uguale al Web 2.0 |
| lato server | pagine generate dal server (CGI, servlet, JSP): ogni clic ricarica tutta la pagina | servizi web (REST) che restituiscono dati (JSON) | dati pubblicati in RDF, collegati fra loro (linked data) |
| esempio | il sito statico di un dipartimento | un calendario che si aggiorna senza ricaricare | un catalogo i cui dati altri siti possono interrogare con SPARQL |
- Nel Web 1.0 il browser è un visualizzatore: manda un form, il server risponde con una nuova pagina HTML completa. Tutta la logica è sul server (CGI e applicazioni web dinamicheLa Common Gateway Interface (CGI, RFC 3875) e' l'interfaccia fra un server web e un programma esterno che genera la risposta: per ogni richiesta il server fa fork ed exec del programma, gli passa i dati con le meta-variabili d'ambiente (REQUEST_METHOD, QUERY_STRING, CONTENT_LENGTH, CONTENT_TYPE, SCRIPT_NAME, PATH_INFO, SERVER_*, REMOTE_ADDR, HTTP_* per gli header) e con lo standard input (corpo della POST, CONTENT_LENGTH byte); il programma scrive sullo standard output header CGI (Content-Type obbligatorio, Status, Location), una riga vuota e il corpo, e il server li trasforma in una risposta HTTP completa; i punti delicati sono pipe e dup2, la chiusura delle estremita' inutilizzate, il timeout, i limiti di dimensione e la sicurezza (injection, path traversal, header Proxy, shellshock); il costo di un processo per richiesta ha portato a FastCGI, ai moduli del server e ai server applicativi.CGI e applicazioni web dinamiche →). Per prenotare un'aula si compila un form,
POST, e si ricevono di nuovo l'elenco e le aule. - Nel Web 2.0 la pagina viene caricata una volta; con AJAX (Asynchronous JavaScript and XML, oggi quasi sempre con JSON e
fetch) JavaScript chiede i dati al server in background e aggiorna solo la parte interessata del DOM. Il server diventa un servizio (Web service, XML, JSON, SOAP e RESTUn web service e' un servizio software accessibile via rete da altri programmi, con messaggi in formato standard su HTTP; i formati di dati sono XML (marcatori annidati, attributi, namespace, validazione con XSD) e JSON (RFC 8259: oggetti, array, stringhe, numeri, true, false, null; piu' compatto e diretto per i linguaggi di programmazione); SOAP e' un protocollo di messaggi XML (Envelope con Header facoltativo e Body, Fault per gli errori) inviati di solito con una POST HTTP e descritti da un file WSDL; REST e' uno stile architetturale (Fielding) in cui il servizio espone risorse identificate da URI e le manipola con i metodi HTTP: GET legge, POST crea, PUT sostituisce, PATCH modifica, DELETE elimina, con codici di stato significativi (200, 201 con Location, 204, 400, 404, 409), senza stato sul server, con cache e interfaccia uniforme.Web service, XML, JSON, SOAP e REST →) e si ripensa il paradigma MVC: la vista è nel browser. È l'architettura dell'esempio. - Nel Web 3.0 il passo successivo è dare ai dati un significato esplicito, perché programmi diversi possano combinarli: ogni cosa ha un identificatore (un URI), i fatti si scrivono come triple soggetto, predicato, oggetto (RDF). È l'ultimo tema del programma e ha una nota a parte.
Un'interazione completa nell'esempio
L'utente apre https://aule.example.org/ e vede le prenotazioni di oggi.
- Il browser fa
GET /(e poi/prenotazioni.css,/prenotazioni.js). Il web server risponde con i file statici. - Il browser costruisce DOM e CSSOM e disegna la pagina senza tabella; lo script (con
defer) parte a fine parsing. - Lo script fa
fetch("/api/prenotazioni?giorno=2026-10-19"). Il web server inoltra la richiesta all'application server. - La servlet
PrenotazioniServletlegge il parametro, chiamaPrenotazioneDAO, che esegue la query parametrizzataSELECT ... WHERE giorno = ?sul DBMS. - La servlet trasforma le righe in JSON e risponde
200 OK. - Lo script riceve l'array, costruisce le righe della tabella con il DOM e il browser fa layout e paint solo per la parte nuova.
Tre richieste HTTP, tre livelli, nessun ricaricamento della pagina.
Errori tipici
- Credere che il browser "esegua" il server: HTML e CSS sono dati, JavaScript viene eseguito nel browser, il Java delle servlet nel container.
- Mettere la validazione solo in JavaScript: il controllo è aggirabile, va rifatto sul server.
- Dimenticare che HTTP è senza stato: senza cookie di sessione il server non riconosce l'utente.
- Confondere web server e application server: il primo serve file e inoltra, il secondo esegue codice.
- Mettere uno
<script>bloccante in cima al<head>e stupirsi che la pagina compaia in ritardo (usaredefer). - Pensare che il DOM sia identico al sorgente: il parser corregge gli errori.
- Aprire una pagina con
fetchdafile://e vedere fallire le richieste: l'origine è diversa, servono pagine servite da un server.
Domande d'esame
- Descrivere l'architettura a tre livelli di un'applicazione web e dire dove va fatta la validazione dei dati. Presentazione (browser), logica applicativa (application server), dati (DBMS); ogni livello parla con l'adiacente. La validazione nel browser è comodità, quella obbligatoria è sul server, perché il client non è fidato.
- Che cosa succede dal momento in cui il browser riceve l'HTML a quando la pagina è visibile? Decodifica e tokenizzazione, DOM, CSSOM, render tree, layout, paint e composizione; gli script senza
defer/asyncbloccano il parsing, i fogli di stile bloccano il rendering. - Differenza fra web server e application server, e fra Web 1.0 e Web 2.0. Il web server serve file e inoltra; l'application server (per esempio Tomcat) esegue le servlet. Nel Web 1.0 il server genera pagine intere a ogni azione, nel Web 2.0 il browser carica una volta la pagina e scambia dati (AJAX, JSON, REST) con il server.
Versione ripasso
Esempio conduttore. Prenotazione aule: tabelle aula(id, nome, posti) e prenotazione(id, aula_id, giorno, ora_inizio, ora_fine, studente); JSON a /api/aule e /api/prenotazioni?giorno=....
Client-server e HTTP. Il client (browser) apre la conversazione, il server risponde (HTTP 1.1 - connessioni persistenti, Content-Length e chunked transfer encodingHTTP/1.1 (oggi RFC 9110 e 9112) rende la connessione persistente di default (si chiude solo con "Connection: close"), rende obbligatorio l'header Host (virtual hosting) e introduce i nuovi metodi PUT, DELETE, OPTIONS, TRACE, Expect: 100-continue, richieste di intervalli (206) e Transfer-Encoding: chunked; con la connessione persistente il client deve sapere dove finisce ogni risposta: lunghezza del corpo nell'ordine HEAD/1xx/204/304 senza corpo, Transfer-Encoding chunked, Content-Length, altrimenti fino alla chiusura; il chunked divide il corpo in blocchi preceduti dalla lunghezza in esadecimale, termina con un chunk 0 e un trailer facoltativo, e si decodifica contando i byte dichiarati (non cercando CRLF).HTTP 1.1 - connessioni persistenti, Content-Length e chunked transfer encoding →, Caching, autenticazione, tipi MIME e URI in HTTPLa cache HTTP riusa le risposte senza contattare il server finche' sono fresche (Cache-Control: max-age, Expires; in mancanza una durata euristica pari a circa il 10% del tempo trascorso da Last-Modified) e, quando sono scadute, le rivalida con una richiesta condizionale (If-None-Match con ETag, If-Modified-Since con Last-Modified) alla quale il server risponde 304 senza corpo; no-store vieta di memorizzare, no-cache obbliga a rivalidare, private esclude le cache condivise. L'autenticazione usa 401 con WWW-Authenticate e la risposta Authorization: Basic (base64 di utente:password, solo sopra TLS) o Digest (hash con nonce); 407 vale per i proxy. Content-Type porta il tipo MIME (tipo/sottotipo; parametri come charset e boundary), Accept e gli altri header Accept-* guidano la negoziazione. Un URI (RFC 3986) e' scheme://userinfo@host:porta/percorso?query#frammento, con percent-encoding %HH per i byte non ammessi e regole per risolvere i riferimenti relativi.Caching, autenticazione, tipi MIME e URI in HTTP →). HTTP è senza stato: l'utente si riconosce con un cookie di sessione. Il server non spedisce dati di sua iniziativa (WebSocket, QUIC e HTTP-3HTTP e' richiesta e risposta, quindi poco adatto a notifiche e dati in tempo reale (polling, long polling, SSE); WebSocket (RFC 6455) apre con un handshake HTTP/1.1 Upgrade (Sec-WebSocket-Key a 16 byte casuali in Base64, risposta 101 con Sec-WebSocket-Accept = Base64 di SHA-1 di chiave piu' GUID fisso) un canale bidirezionale persistente sulla stessa connessione TCP, con frame di 2-14 byte di intestazione (FIN, opcode, MASK, lunghezza su 7, 16 o 64 bit, chiave di mascheramento obbligatoria dal client al server) e messaggi di testo, binari, close, ping e pong. QUIC (RFC 9000) e' un protocollo di trasporto sopra UDP con TLS 1.3 integrato, flussi indipendenti (niente head-of-line blocking fra flussi), connection ID che permettono di cambiare rete, handshake in 1 RTT e ripresa in 0 RTT; HTTP/3 (RFC 9114) mappa HTTP su QUIC con un flusso per richiesta e QPACK per gli header, ed e' annunciato con Alt-Svc.WebSocket, QUIC e HTTP-3 →).
- presentazione: HTML, CSS, JavaScript nel browser;
- logica: servlet sull'application server;
- dati: DBMS (
AulaDAO,PrenotazioneDAO). Ogni livello parla solo con l'adiacente. Vantaggi: sostituibilità, scalabilità, sicurezza centralizzata. Livello = divisione fisica, strato = divisione logica del codice. Client sottile = pagine pronte dal server; client spesso = logica in JavaScript.
Web server e application server. Web server (httpd, nginx): file statici, HTTPS, cache, reverse proxy. Application server / container (Tomcat): esegue codice (servlet), sessioni, ciclo di vita (CGI e applicazioni web dinamicheLa Common Gateway Interface (CGI, RFC 3875) e' l'interfaccia fra un server web e un programma esterno che genera la risposta: per ogni richiesta il server fa fork ed exec del programma, gli passa i dati con le meta-variabili d'ambiente (REQUEST_METHOD, QUERY_STRING, CONTENT_LENGTH, CONTENT_TYPE, SCRIPT_NAME, PATH_INFO, SERVER_*, REMOTE_ADDR, HTTP_* per gli header) e con lo standard input (corpo della POST, CONTENT_LENGTH byte); il programma scrive sullo standard output header CGI (Content-Type obbligatorio, Status, Location), una riga vuota e il corpo, e il server li trasforma in una risposta HTTP completa; i punti delicati sono pipe e dup2, la chiusura delle estremita' inutilizzate, il timeout, i limiti di dimensione e la sicurezza (injection, path traversal, header Proxy, shellshock); il costo di un processo per richiesta ha portato a FastCGI, ai moduli del server e ai server applicativi.CGI e applicazioni web dinamiche →). Pagina dinamica = prodotta al momento della richiesta (server) o modificata dopo il caricamento (client).
Pipeline del browser.
- rete (DNS, TCP/TLS, GET);
- decodifica e tokenizzazione;
- DOM (il parser corregge gli errori, non fallisce);
- CSSOM e stile calcolato;
- render tree (solo nodi visibili);
- layout (reflow);
- paint e composizione.
Blocchi. Script classico: blocca il parsing. defer: dopo il parsing, in ordine. async: appena scaricato. type="module": come defer. Il CSS blocca il rendering. DOMContentLoaded = DOM pronto; load = tutto caricato.
Origine = schema + host + porta; la same-origin policy impedisce di leggere risposte di altre origini (si apre con CORS).
Web 1.0 pagine intere generate dal server, ogni clic ricarica. Web 2.0 AJAX/fetch, JSON, servizi REST (Web service, XML, JSON, SOAP e RESTUn web service e' un servizio software accessibile via rete da altri programmi, con messaggi in formato standard su HTTP; i formati di dati sono XML (marcatori annidati, attributi, namespace, validazione con XSD) e JSON (RFC 8259: oggetti, array, stringhe, numeri, true, false, null; piu' compatto e diretto per i linguaggi di programmazione); SOAP e' un protocollo di messaggi XML (Envelope con Header facoltativo e Body, Fault per gli errori) inviati di solito con una POST HTTP e descritti da un file WSDL; REST e' uno stile architetturale (Fielding) in cui il servizio espone risorse identificate da URI e le manipola con i metodi HTTP: GET legge, POST crea, PUT sostituisce, PATCH modifica, DELETE elimina, con codici di stato significativi (200, 201 con Location, 204, 400, 404, 409), senza stato sul server, con cache e interfaccia uniforme.Web service, XML, JSON, SOAP e REST →). Web 3.0 dati con significato (RDF, linked data).
Errori tipici
- Validare solo nel browser.
- Dimenticare che HTTP è senza stato.
- Confondere web server e application server.
- Script bloccante senza
defer. - Pensare che il DOM coincida col sorgente.
Seguito: Linguaggi di markup - SGML, HTML e XMLUn linguaggio di markup aggiunge al testo marcatori che ne descrivono la struttura. SGML (ISO 8879, 1986) è il metalinguaggio da cui derivano HTML fino alla 4.01 e XML 1.0 (W3C, 1998, un sottoinsieme semplificato); HTML5 ha un proprio algoritmo di parsing, tollerante, e non è né SGML né XML. Un documento XML è ben formato se rispetta la sintassi (un'unica radice, tag annidati e chiusi, attributi tra virgolette, nomi con maiuscole e minuscole distinte, entità per i caratteri speciali) ed è valido se in più rispetta uno schema. La DTD dichiara elementi (ELEMENT: EMPTY, ANY, #PCDATA, sequenza con la virgola, scelta con |, cardinalità ? * +) e attributi (ATTLIST: CDATA, ID, IDREF, enumerazioni; #REQUIRED, #IMPLIED, #FIXED); non ha tipi di dato e non conosce i namespace. XML Schema (XSD) è scritto in XML e ha tipi (xs:date, xs:positiveInteger), restrizioni con pattern, minOccurs e maxOccurs, key e keyref. I namespace (xmlns, xmlns:prefisso) identificano il vocabolario con un URI; gli attributi senza prefisso non appartengono a nessun namespace. XML è rigido e adatto a documenti e configurazioni (web.xml, pom.xml), HTML è tollerante e ha un vocabolario fisso; JSON è l'alternativa più compatta per scambiare dati e non ha attributi, schema standard o contenuto misto.Linguaggi di markup - SGML, HTML e XML →, HTML5 - struttura del documento, elementi semantici e formUn documento HTML5 ha la forma doctype (), html con lang, head (meta charset UTF-8, viewport, title, link, script) e body. Il parser HTML5 non dà mai errore: corregge (aggiunge il tbody, chiude i p). Gli elementi semantici (header, nav, main, section, article, aside, footer, figure, time) descrivono il ruolo del contenuto e danno ai lettori di schermo i punti di riferimento; div e span sono neutri. Link: a href (assoluto, relativo, #frammento, mailto), rel e target; immagini con alt obbligatorio. Tabelle: table, caption, thead, tbody, th con scope, td, colspan e rowspan, solo per dati. Form: action, method (GET mette i campi nella query string, per letture idempotenti e ricercabili; POST li mette nel corpo, per modificare lo stato), enctype (application/x-www-form-urlencoded predefinito, multipart/form-data per i file); si invia solo ciò che ha name; label for collega etichetta e campo; tipi di input (text, email, number, date, time, radio, checkbox, hidden...); validazione nativa con required, minlength, maxlength, min, max, step, pattern e type, aggirabile e quindi da ripetere sul server; novalidate la spegne. Accessibilità: lang, alt, label, gerarchia dei titoli, navigazione da tastiera, ARIA solo quando l'HTML nativo non basta.HTML5 - struttura del documento, elementi semantici e form →, CSS3 - selettori, cascata, box model e layoutCSS descrive la presentazione: regole selettore { proprietà: valore }. Selettori: tipo, classe (.a), id (#a), attributo ([type="date"]), universale, combinatori (spazio discendente, > figlio, + fratello adiacente, ~ fratelli seguenti), pseudo-classi (:hover, :focus-visible, :nth-child, :not, :checked) e pseudo-elementi (::before, ::after). Cascata: tra più regole che toccano la stessa proprietà vince, nell'ordine, l'importanza (!important vince, e lo stile inline batte gli stili normali), poi la specificità (a,b,c) = (id, classi+attributi+pseudo-classi, tipi+pseudo-elementi) confrontata come numeri uno alla volta da sinistra, infine l'ordine di comparsa (l'ultima vince). Ereditarietà: color, font, line-height, text-align passano ai figli; margin, padding, border, background, width no; inherit, initial, unset la controllano. Box model: content, padding, border, margin; con content-box width è solo il contenuto, con border-box include padding e bordo; i margini verticali adiacenti collassano nel maggiore. display: block, inline, inline-block, none, flex, grid. position: static, relative, absolute (rispetto all'antenato posizionato), fixed, sticky. Flexbox è per una dimensione (justify-content, align-items, gap, flex-grow, flex-wrap), grid per due (grid-template-columns, fr, repeat, minmax, gap). Responsive: meta viewport, unità relative (rem, %, fr), media query @media (min-width), approccio mobile-first.CSS3 - selettori, cascata, box model e layout →, JavaScript - linguaggio, DOM ed eventiJavaScript (standard ECMAScript, Ecma-262) è il linguaggio dei browser: tipizzato dinamicamente, con sette tipi primitivi (number, string, boolean, undefined, null, bigint, symbol) e gli oggetti (anche array e funzioni); typeof null vale "object" per un errore storico; == converte i tipi, === no (si usa sempre ===); valori falsy: false, 0, 0n, "", null, undefined, NaN. let e const hanno visibilità di blocco e una zona morta prima della dichiarazione, var visibilità di funzione e hoisting; const impedisce di riassegnare il nome, non di modificare l'oggetto. Funzioni: dichiarazione (hoisted), espressione, arrow function (this lessicale), parametri predefiniti, rest e spread, destructuring. Array: map, filter, reduce, find, some, every, sort (senza comparatore ordina come stringhe). Closure: una funzione ricorda le variabili dell'ambiente in cui è nata. Eventi: addEventListener; propagazione in tre fasi (cattura, target, bolla), stopPropagation, preventDefault, delegazione con event.target e closest. DOM: albero di nodi; querySelector, createElement, append, replaceChildren, textContent (sicuro) contro innerHTML (rischio di XSS), classList, dataset. Moduli ES (type="module", import e export) sono differiti e in modalità strict. Event loop: un solo thread, call stack, coda dei task (setTimeout, eventi) e coda dei microtask (promise, await) svuotata dopo ogni task: l'esecuzione asincrona (fetch, async e await, Promise.all) è alla base di AJAX e il codice lungo blocca la pagina.JavaScript - linguaggio, DOM ed eventi →. Schema dei livelli.
| livello | compito | nell'esempio |
|---|---|---|
| presentazione | mostrare, raccogliere input | HTML, CSS, JavaScript nel browser |
| logica | regole, autenticazione, formato delle risposte | AuleServlet, PrenotazioniServlet |
| dati | memorizzare e interrogare | tabelle aula, prenotazione; AulaDAO, PrenotazioneDAO |
Il browser non apre mai connessioni al database; ogni controllo fatto in JavaScript va rifatto sul server. Due livelli = client che parla col DB (credenziali esposte, client da aggiornare ovunque); tre o più = aggiunte di cache, autenticazione, altri servizi.
Scambio HTTP dell'esempio.
GET /api/prenotazioni?giorno=2026-10-19 HTTP/1.1
Host: aule.example.org
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 207Corpo: array JSON con le prenotazioni 1 e 2. HTTP è senza stato: la sessione dell'utente sta sul server, il cookie ne è la chiave (Caching, autenticazione, tipi MIME e URI in HTTPLa cache HTTP riusa le risposte senza contattare il server finche' sono fresche (Cache-Control: max-age, Expires; in mancanza una durata euristica pari a circa il 10% del tempo trascorso da Last-Modified) e, quando sono scadute, le rivalida con una richiesta condizionale (If-None-Match con ETag, If-Modified-Since con Last-Modified) alla quale il server risponde 304 senza corpo; no-store vieta di memorizzare, no-cache obbliga a rivalidare, private esclude le cache condivise. L'autenticazione usa 401 con WWW-Authenticate e la risposta Authorization: Basic (base64 di utente:password, solo sopra TLS) o Digest (hash con nonce); 407 vale per i proxy. Content-Type porta il tipo MIME (tipo/sottotipo; parametri come charset e boundary), Accept e gli altri header Accept-* guidano la negoziazione. Un URI (RFC 3986) e' scheme://userinfo@host:porta/percorso?query#frammento, con percent-encoding %HH per i byte non ammessi e regole per risolvere i riferimenti relativi.Caching, autenticazione, tipi MIME e URI in HTTP →).
Web server e application server.
| web server | application server / container | |
|---|---|---|
| esempi | Apache httpd, nginx | Tomcat (servlet) |
| compito | file statici, HTTPS, cache, reverse proxy | esegue il codice, sessioni, ciclo di vita |
Statica = file così com'è; dinamica = prodotta alla richiesta (CGI, servlet, JSP) oppure modificata dal browser dopo il caricamento.
Interazione completa.
GET /e file statici dal web server.- DOM e CSSOM, prima pagina disegnata; lo script
deferparte a fine parsing. fetch("/api/prenotazioni?giorno=...")inoltrata all'application server.PrenotazioniServletePrenotazioneDAOeseguono la query parametrizzata sul DBMS.- Risposta
200con JSON. - Lo script costruisce le righe con il DOM; il browser rifà layout e paint solo della parte nuova.
Domande d'esame, tracce.
- Tre livelli e dove si valida: la validazione obbligatoria è sul server, quella nel browser è comodità.
- Dall'HTML alla pagina: DOM, CSSOM, render tree, layout, paint;
defereasync. - Web server contro application server; Web 1.0 contro 2.0: pagine intere contro dati scambiati con AJAX.
Esercizi su questo argomento
- Esercizio - Git con branch, merge e conflitto (applicazione web)
- Esercizio - Servlet delle prenotazioni con controllo delle sovrapposizioni (applicazione web)
- Esercizio - Tabella delle prenotazioni costruita con il DOM e filtrata (applicazione web)
- Esercizio - XML ben formato e valido con una DTD per le prenotazioni (applicazione web)
Teoria collegata
- AJAX e fetch - applicazioni Web 2.0 e MVC rivisto
- CSS3 - selettori, cascata, box model e layout
- HTML5 - struttura del documento, elementi semantici e form
- Java servlet e Apache Tomcat - ciclo di vita, richieste e sessioni
- JavaScript - linguaggio, DOM ed eventi
- Linguaggi di markup - SGML, HTML e XML
- Strumenti di sviluppo - git, Maven e Docker
- Web semantico - RDF, linked open data e SPARQL