Esercizio - Form HTML di prenotazione con validazione nativa e in JavaScript (applicazione web)
In questa pagina 4
Testo. Nell'applicazione di prenotazione delle aule (tabelle aula(id, nome, posti) e prenotazione(id, aula_id, giorno, ora_inizio, ora_fine, studente)) serve una pagina con il modulo per prenotare un'aula. Richieste:
- campi: aula (scelta fra le tre aule), giorno, ora di inizio, ora di fine, cognome dello studente; i nomi dei campi sono quelli delle colonne;
- validazione nativa HTML: tutti i campi obbligatori; il giorno non nel passato; le ore fra le 08:00 e le 20:00 a passi di mezz'ora; il cognome di 2-40 caratteri, solo lettere, apostrofo e spazio;
- validazione in JavaScript per i controlli che HTML non sa fare: l'ora di fine deve seguire quella di inizio e l'aula non deve essere già prenotata nello stesso intervallo;
- all'invio valido, la pagina non ricarica ma costruisce l'oggetto JSON da mandare al server.
Teoria: 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 → (form, tipi di input, validazione nativa, GET e POST), 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 → (eventi, preventDefault, array). Si ricorda che i controlli nel browser sono solo una comodità: il server li deve rifare (Organizzazione fisica dei dati e accesso programmatico alle basi di datiUn DBMS è formato da un gestore delle connessioni, un parser, un ottimizzatore che sceglie il piano di esecuzione usando le statistiche del catalogo, un esecutore, i metodi di accesso (heap, ordinato, hash, B-tree, indici), il buffer manager e il gestore delle transazioni (lock e log). Il costo si misura in accessi a blocchi del disco. L'ottimizzatore riscrive l'espressione algebrica (selezioni e proiezioni il prima possibile, prodotto con selezione = join, ordine dei join) e sceglie gli algoritmi: scansione o indice per le selezioni, nested loop, block nested loop, index nested loop, sort-merge o hash join per i join (con 1000 e 100 blocchi, 10 pagine di buffer: 11 mila accessi con block nested loop, 3300 con hash join). La progettazione fisica sceglie organizzazione dei file, indici ed eventuale denormalizzazione in base al carico. Le applicazioni accedono ai dati con SQL incorporato, interfacce a livello di chiamata (ODBC, JDBC, DB-API di Python), ORM o routine memorizzate: connessione, cursore, execute con parametri, fetch, commit o rollback. Mai concatenare l'input dell'utente nell'SQL (SQL injection): query parametrizzate. Un'applicazione web è a tre livelli: browser, server applicativo (HTTP senza stato, sessioni e cookie) e DBMS.Organizzazione fisica dei dati e accesso programmatico alle basi di dati →). Il codice è stato provato in un browser (Edge) e la logica pura anche con node.
Parte 1 - Il form HTML con la validazione nativa
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Prenota un'aula</title>
</head>
<body>
<h1>Prenota un'aula</h1>
<form id="prenota" action="/prenotazioni" method="post">
<p>
<label for="aula_id">Aula</label>
<select id="aula_id" name="aula_id" required>
<option value="">Scegli...</option>
<option value="1">Aula A1 (80 posti)</option>
<option value="2">Aula B2 (45 posti)</option>
<option value="3">Lab Info (30 posti)</option>
</select>
</p>
<p>
<label for="giorno">Giorno</label>
<input type="date" id="giorno" name="giorno" required>
</p>
<p>
<label for="ora_inizio">Dalle</label>
<input type="time" id="ora_inizio" name="ora_inizio" required
min="08:00" max="19:00" step="1800">
<label for="ora_fine">alle</label>
<input type="time" id="ora_fine" name="ora_fine" required
min="09:00" max="20:00" step="1800">
</p>
<p>
<label for="studente">Cognome</label>
<input type="text" id="studente" name="studente" required
minlength="2" maxlength="40"
pattern="[A-Za-zÀ-ÿ' ]+"
title="Solo lettere, apostrofo e spazio">
</p>
<p><button type="submit">Prenota</button></p>
<p id="esito" role="status"></p>
</form>
<script src="prenota.js"></script>
</body>
</html>Spiegazione:
method="post": la prenotazione crea una riga, non è una lettura idempotente.action="/prenotazioni"è l'indirizzo che la servletPrenotazioniServletesporrà.- Ogni campo ha
label foruguale all'id, e ilnameuguale alla colonna: senzanameil campo non verrebbe inviato. - La
selectha una primaoptionconvalue=""("Scegli..."): conrequiredil browser considera "vuoto" proprio quel valore, e costringe a scegliere un'aula vera. Le altreoptionhanno comevaluel'iddell'aula, che è ciò che viaggia. type="date"etype="time"danno i selettori nativi e invianoAAAA-MM-GGeHH:MM, gli stessi formati delle colonne SQL.min,maxestep="1800"(secondi: 30 minuti) limitano le ore:ora_inizioda 08:00 a 19:00,ora_fineda 09:00 a 20:00.minlength="2" maxlength="40"per la lunghezza;pattern="[A-Za-zÀ-ÿ' ]+"deve coincidere con tutto il valore: lettere (anche accentate), apostrofo, spazio.titleè il testo che il browser aggiunge al messaggio d'errore.<p id="esito" role="status">è la zona del messaggio di esito:role="status"fa leggere ai lettori di schermo il testo quando cambia.- Il giorno minimo non si può scrivere nell'HTML (cambia ogni giorno): lo imposta JavaScript (parte 3).
Che cosa controlla il browser. Verificato impostando valori diversi e leggendo validity:
| valore | campo | stato |
|---|---|---|
| campo vuoto | aula, giorno, cognome | valueMissing |
| 07:00 | ora_inizio | rangeUnderflow |
| 09:10 | ora_inizio | stepMismatch (non è un multiplo di 30 minuti dall'inizio dell'intervallo) |
| 09:30 | ora_inizio | valido |
| 20:30 | ora_fine | rangeOverflow |
R2d2 |
cognome | patternMismatch |
Dall'Olio Anna |
cognome | valido |
| 2020-01-01 | giorno (con min impostato) |
rangeUnderflow |
Il browser blocca l'invio e mostra un messaggio sul primo campo non valido. Due dettagli: minlength (e maxlength) sono controllati solo dopo una modifica fatta dall'utente, non su un valore impostato da codice (provato: "R" impostato con JavaScript risulta valido); inoltre la validazione nativa non confronta due campi fra loro.
Parte 2 - Che cosa viaggia
Compilando aula B2, giorno 2026-10-22, dalle 09:30 alle 10:30, cognome D'Angelo e premendo "Prenota" (senza JavaScript), il browser invia:
POST /prenotazioni HTTP/1.1
Content-Type: application/x-www-form-urlencoded
aula_id=2&giorno=2026-10-22&ora_inizio=09%3A30&ora_fine=10%3A30&studente=D%27Angelo- I campi sono
nome=valoreseparati da&, nell'ordine del form. L'apostrofo diventa%27e i due punti%3A(percent-encoding; lo spazio sarebbe+: 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 →). - Con
method="get"gli stessi campi andrebbero nell'URI (/prenotazioni?aula_id=2&giorno=...): sbagliato per un'operazione che modifica dati, e il cognome resterebbe nei log. - Quanto al valore dell'aula: il server riceve
2come testo e lo converte in intero.
Parte 3 - Validazione in JavaScript
HTML non sa dire "la fine è dopo l'inizio" né "l'aula è libera": lo fa JavaScript, con l'API di validazione dei vincoli, in particolare setCustomValidity(messaggio): un messaggio non vuoto rende il campo non valido (customError) e il browser lo mostra come i suoi; con "" il campo torna valido.
// Prenotazioni già presenti (in produzione arrivano dal server).
const esistenti = [
{ 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" },
{ id: 3, aula_id: 2, giorno: "2026-10-20", ora_inizio: "10:00", ora_fine: "12:00", studente: "Verdi" },
{ id: 4, aula_id: 3, giorno: "2026-10-21", ora_inizio: "09:00", ora_fine: "13:00", studente: "Rossi" }
];
// Due intervalli [i1,f1) e [i2,f2) si sovrappongono se ciascuno inizia prima che l'altro finisca.
// Le ore "HH:MM" si confrontano come stringhe: l'ordine alfabetico coincide con quello temporale.
function siSovrappone(a, b) {
return a.ora_inizio < b.ora_fine && b.ora_inizio < a.ora_fine;
}
// Restituisce il messaggio d'errore, oppure "" se la richiesta è accettabile.
function controlla(nuova, esistenti) {
if (nuova.ora_fine <= nuova.ora_inizio) {
return "L'ora di fine deve essere dopo l'ora di inizio.";
}
const conflitto = esistenti.find(p =>
p.aula_id === nuova.aula_id && p.giorno === nuova.giorno && siSovrappone(p, nuova));
if (conflitto) {
return "Aula già prenotata dalle " + conflitto.ora_inizio + " alle " + conflitto.ora_fine + ".";
}
return "";
}
const form = document.getElementById("prenota");
const campi = form.elements; // accesso per name
const esito = document.getElementById("esito");
// Giorno minimo = oggi (data locale in formato AAAA-MM-GG).
const oggi = new Date();
campi.giorno.min = oggi.getFullYear() + "-" +
String(oggi.getMonth() + 1).padStart(2, "0") + "-" +
String(oggi.getDate()).padStart(2, "0");
function leggi() {
return {
aula_id: Number(campi.aula_id.value),
giorno: campi.giorno.value,
ora_inizio: campi.ora_inizio.value,
ora_fine: campi.ora_fine.value,
studente: campi.studente.value.trim()
};
}
function validaIntervallo() {
campi.ora_fine.setCustomValidity(""); // azzera: altrimenti il campo resta invalido
if (campi.ora_inizio.value && campi.ora_fine.value) {
campi.ora_fine.setCustomValidity(controlla(leggi(), esistenti));
}
}
for (const nome of ["aula_id", "giorno", "ora_inizio", "ora_fine"]) {
campi[nome].addEventListener("input", validaIntervallo);
}
form.addEventListener("submit", e => {
e.preventDefault(); // niente invio classico: si userà fetch
validaIntervallo();
if (!form.reportValidity()) return; // mostra i messaggi del browser e si ferma
esito.textContent = "Invio: " + JSON.stringify(leggi());
});Spiegazione:
siSovrappone: due intervalli (aperti a destra) si sovrappongono se ciascuno inizia prima che l'altro finisca. Se uno finisce esattamente quando l'altro inizia (11:00 e 11:00), non si sovrappongono: si può prenotare dalle 11:00 dopo uno slot che finisce alle 11:00.- Confronto fra stringhe
"HH:MM": con ore a due cifre l'ordine alfabetico coincide con quello temporale ("09:00" < "14:00"), senza convertire. Non varrebbe per"9:00": per questo l'inputtype="time"è utile, perché restituisce sempre due cifre. controllaè una funzione pura (nessun DOM): per questo si prova da sola. Confrontaaula_idcome numero:leggi()converte il valore delselect(una stringa) conNumber.validaIntervallo: primasetCustomValidity("")per togliere un errore precedente (altrimenti, una volta impostato un messaggio, il campo resta non valido anche dopo la correzione), poi imposta il messaggio solo se i campi hanno un valore. Lo si lega aora_fineperché il messaggio compaia su quel campo.- Il gestore
inputdi tutti i campi coinvolti ricalcola la validità a ogni modifica. submit:preventDefault()ferma l'invio e il ricaricamento;validaIntervallo()ricalcola (l'utente potrebbe non aver toccato nulla dopo l'ultimo cambio);reportValidity()esegue tutti i controlli (nativi e personalizzati), mostra l'errore del primo campo non valido e restituiscefalse.- Il giorno minimo si imposta da JavaScript:
getMonth()parte da 0, per questo+ 1;padStart(2, "0")produceAAAA-MM-GGcon zeri. - Alla fine
JSON.stringify(leggi())produce il corpo che si invierebbe confetch(POSTconContent-Type: application/json, 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 →); in produzione la risposta del server ha l'ultima parola.
Prove di controlla con le prenotazioni esistenti (node):
| aula | giorno | dalle | alle | risultato |
|---|---|---|---|---|
| 1 | 2026-10-19 | 08:00 | 09:00 | accettata (finisce quando comincia la prenotazione 1) |
| 1 | 2026-10-19 | 10:00 | 12:00 | "Aula già prenotata dalle 09:00 alle 11:00." |
| 2 | 2026-10-19 | 10:00 | 12:00 | accettata (altra aula) |
| 1 | 2026-10-19 | 11:00 | 12:00 | accettata (comincia quando finisce la prenotazione 1) |
| 1 | 2026-10-19 | 09:30 | 09:30 | "L'ora di fine deve essere dopo l'ora di inizio." |
| 1 | 2026-10-19 | 15:00 | 17:00 | "Aula già prenotata dalle 14:00 alle 16:00." |
| 1 | 2026-10-19 | 12:00 | 14:00 | accettata (tra le due) |
| 1 | 2026-10-19 | 08:00 | 18:00 | "Aula già prenotata dalle 09:00 alle 11:00." (contiene la prenotazione 1) |
Prova nel browser. Con il form compilato aula 1, 2027-12-01, 09:30-10:30, D'Angelo, requestSubmit() scrive nella zona dell'esito Invio: {"aula_id":1,"giorno":"2027-12-01","ora_inizio":"09:30","ora_fine":"10:30","studente":"D'Angelo"}. Con aula 1, 2026-10-19, 10:00-12:00 il campo ora_fine risulta in errore con il messaggio sull'aula già prenotata, e con fine prima dell'inizio, quello sull'ora di fine.
Parte 4 - E il server?
La pagina dà un'esperienza comoda, ma non protegge i dati: chiunque può mandare un POST con curl, senza form e senza JavaScript. Il server deve quindi rifare tutti i controlli:
- campi presenti e di tipo giusto (
aula_idintero che esiste inaula,giornodata valida, ore nel formatoHH:MM); ora_fine > ora_inizio(anche il database lo impone conCHECK, 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 →);- nessuna sovrapposizione con le prenotazioni già salvate nello stesso istante dell'inserimento (il browser conosce solo i dati caricati prima: un altro utente potrebbe aver prenotato nel frattempo);
- uso di query parametrizzate per salvare il cognome (Organizzazione fisica dei dati e accesso programmatico alle basi di datiUn DBMS è formato da un gestore delle connessioni, un parser, un ottimizzatore che sceglie il piano di esecuzione usando le statistiche del catalogo, un esecutore, i metodi di accesso (heap, ordinato, hash, B-tree, indici), il buffer manager e il gestore delle transazioni (lock e log). Il costo si misura in accessi a blocchi del disco. L'ottimizzatore riscrive l'espressione algebrica (selezioni e proiezioni il prima possibile, prodotto con selezione = join, ordine dei join) e sceglie gli algoritmi: scansione o indice per le selezioni, nested loop, block nested loop, index nested loop, sort-merge o hash join per i join (con 1000 e 100 blocchi, 10 pagine di buffer: 11 mila accessi con block nested loop, 3300 con hash join). La progettazione fisica sceglie organizzazione dei file, indici ed eventuale denormalizzazione in base al carico. Le applicazioni accedono ai dati con SQL incorporato, interfacce a livello di chiamata (ODBC, JDBC, DB-API di Python), ORM o routine memorizzate: connessione, cursore, execute con parametri, fetch, commit o rollback. Mai concatenare l'input dell'utente nell'SQL (SQL injection): query parametrizzate. Un'applicazione web è a tre livelli: browser, server applicativo (HTTP senza stato, sessioni e cookie) e DBMS.Organizzazione fisica dei dati e accesso programmatico alle basi di dati →).
In caso di errore risponde con uno stato 4xx e un messaggio (per esempio 400 per un dato non valido, 409 per un conflitto con un'altra prenotazione), che la pagina mostra nella zona esito.
Errori tipici
- Campo senza
name: non viene inviato. selectconrequiredma senzaoption value=""iniziale: parte già con un'aula scelta.labelsenzaforo non collegata all'id.- Confrontare le ore "9:00" e "14:00" come stringhe: l'ordine alfabetico sbaglia; usare sempre due cifre.
- Non azzerare
setCustomValidity(""): il campo resta invalido anche dopo la correzione. - Dimenticare
preventDefault()nel gestore disubmit. - Confrontare
aula_id(numero) con il valore del campo (stringa) senzaNumber. - Dichiarare "tutto validato" senza rifare i controlli sul server.
Versione ripasso
Form. <form id="prenota" action="/prenotazioni" method="post"> con campi:
aula_id(selectcon primaoption value="",required);giorno(type="date",required);ora_inizio(type="time",min="08:00" max="19:00" step="1800");ora_fine(min="09:00" max="20:00" step="1800");studente(required minlength="2" maxlength="40" pattern="[A-Za-zÀ-ÿ' ]+").
Stati nativi: vuoto valueMissing; 07:00 rangeUnderflow; 09:10 stepMismatch; 20:30 rangeOverflow; R2d2 patternMismatch. minlength conta solo dopo la modifica dell'utente; nessun controllo fra due campi.
Cosa viaggia. POST con application/x-www-form-urlencoded:
aula_id=2&giorno=2026-10-22&ora_inizio=09%3A30&ora_fine=10%3A30&studente=D%27Angelo (: = %3A, ' = %27, spazio = +). GET metterebbe i campi nell'URI: no per modificare dati.
siSovrappone(a, b)=a.ora_inizio < b.ora_fine && b.ora_inizio < a.ora_fine; intervalli adiacenti (fine = inizio) non si sovrappongono.- Le ore
"HH:MM"si confrontano come stringhe (due cifre sempre). controlla(nuova, esistenti)è pura: fine dopo inizio, poi nessuna sovrapposizione nella stessa aula e nello stesso giorno; restituisce il messaggio o"".validaIntervallo:setCustomValidity("")poi il messaggio suora_fine; richiamata a ogniinput.submit:preventDefault(),validaIntervallo(),form.reportValidity(); se validoJSON.stringify(leggi())conaula_idconvertito conNumber.- Giorno minimo = oggi, impostato da JavaScript (
getMonth() + 1,padStart).
Server. I controlli del browser sono solo comodità: il server rifà tutto (tipi, ora_fine > ora_inizio, sovrapposizioni al momento dell'inserimento, query parametrizzate) e risponde 400 o 409 (Organizzazione fisica dei dati e accesso programmatico alle basi di datiUn DBMS è formato da un gestore delle connessioni, un parser, un ottimizzatore che sceglie il piano di esecuzione usando le statistiche del catalogo, un esecutore, i metodi di accesso (heap, ordinato, hash, B-tree, indici), il buffer manager e il gestore delle transazioni (lock e log). Il costo si misura in accessi a blocchi del disco. L'ottimizzatore riscrive l'espressione algebrica (selezioni e proiezioni il prima possibile, prodotto con selezione = join, ordine dei join) e sceglie gli algoritmi: scansione o indice per le selezioni, nested loop, block nested loop, index nested loop, sort-merge o hash join per i join (con 1000 e 100 blocchi, 10 pagine di buffer: 11 mila accessi con block nested loop, 3300 con hash join). La progettazione fisica sceglie organizzazione dei file, indici ed eventuale denormalizzazione in base al carico. Le applicazioni accedono ai dati con SQL incorporato, interfacce a livello di chiamata (ODBC, JDBC, DB-API di Python), ORM o routine memorizzate: connessione, cursore, execute con parametri, fetch, commit o rollback. Mai concatenare l'input dell'utente nell'SQL (SQL injection): query parametrizzate. Un'applicazione web è a tre livelli: browser, server applicativo (HTTP senza stato, sessioni e cookie) e DBMS.Organizzazione fisica dei dati e accesso programmatico alle basi di dati →).
Errori tipici
- Campo senza
name;selectsenzaoption value="". - Non azzerare
setCustomValidity. - Dimenticare
preventDefault(). aula_idstringa contro numero.- Fidarsi solo della validazione nel browser.
Controlli di
controlla(nuova, esistenti)sulle prenotazioni esistenti dell'aula 1 (09-11 e 14-16 del 19 ottobre):
| dalle | alle | esito |
|---|---|---|
| 08:00 | 09:00 | accettata (adiacente) |
| 10:00 | 12:00 | conflitto con 09:00-11:00 |
| 11:00 | 12:00 | accettata (adiacente) |
| 09:30 | 09:30 | fine non dopo l'inizio |
| 12:00 | 14:00 | accettata |
| 08:00 | 18:00 | conflitto (contiene la prima) |
L'aula 2 allo stesso orario è libera: il conflitto riguarda stessa aula e stesso giorno.
Messaggi nativi e personalizzati.
requireddàvalueMissing;minemaxdannorangeUnderflowerangeOverflow;stepdàstepMismatch;patterndàpatternMismatch;setCustomValidity("...")dàcustomError.form.reportValidity()esegue tutti i controlli, mostra il primo errore e restituiscefalse.setCustomValidity("")va richiamata per togliere l'errore precedente.
Esito. Dopo preventDefault() e controlli superati la pagina mostra Invio: {"aula_id":1,"giorno":"2027-12-01","ora_inizio":"09:30","ora_fine":"10:30","studente":"D'Angelo"}; con fetch sarebbe il corpo di un POST application/json.
Server. Risponde 400 per dati non validi e 409 per conflitto; la sovrapposizione va controllata al momento dell'inserimento, perché un altro utente potrebbe aver prenotato nel frattempo.