Capitolo 2 · Sviluppo web con Spring Boot: il corso pratico
Lo sportello del pronto soccorso risponde, ma risponde sempre la stessa cosa. Poi arriva la domanda che fa inciampare tutti: come faccio a dire al server quale paziente sto registrando?
Finché la risposta è fissa, una frase di stato o un numero finto, va tutto liscio. Appena servono dati veri, come un nome, un id o un filtro, saltano fuori tre modi di farli viaggiare, due verbi HTTP e una pioggia di termini: path variable, query string, request param, payload, body.
E a quel punto? Apri tre tutorial e ognuno usa una parola diversa per la stessa cosa, come se tu le conoscessi già tutte.
Mettiamo ordine, partendo da un'immagine che ti accompagnerà per tutto l'articolo: la posta.
Cartolina o lettera: che differenza c'è tra GET e POST?
Immagina di dover spedire un messaggio dall'ambulatorio alla segreteria. Puoi usare una cartolina o una lettera in busta.
La cartolina è la richiesta GET. Sulla facciata c'è tutto, ben visibile: il destinatario (il server), il mittente (tu) e anche il messaggio, che coincide con l'indirizzo stesso. Quando scrivi /pazienti/status, il "cosa voglio" sta già lì, scritto sulla cartolina.
La lettera è la richiesta POST. Fuori c'è l'intestazione (header) con destinatario e mittente, come prima. Dentro c'è una parte in più: il payload, chiamato anche body o data, che può essere piccolo o enorme.
Perché servono tutti e due? Perché la cartolina ha lo spazio che ha. Provare a infilare nell'indirizzo un'intera cartella clinica non funziona: per quello esiste la lettera.
| GET | POST | |
|---|---|---|
| Dove viaggiano i dati | Nell'indirizzo | Nel payload |
| Quanti dati | Pochi | Anche moltissimi |
| Si vedono | Nella barra del browser | Dentro la richiesta |
| Uso tipico | Leggere | Inviare o creare |
C'è anche una questione di riservatezza. Un indirizzo resta nella cronologia del browser e, di solito, finisce nei log dei server: scriverci dentro dati delicati, come quelli di un paziente, non è una grande idea. Attenzione però a non fare l'errore opposto: la POST non è per magia "sicura". Il payload non compare nell'indirizzo, ma non è cifrato. A proteggere la comunicazione, per entrambi i verbi, è l'HTTPS.
Una precisazione che si dimentica spesso: la risposta del server è sempre una sola: una riga di stato (dove sta lo status code), gli header e i dati. GET e POST sono tipi di richiesta, non di risposta.
I due modi di mandare un dato con una GET
Nei vecchi siti dinamici (quelli in PHP o JSP) qualsiasi dato viaggiava così, dentro query string lunghissime del tipo ?azione=modifica&id=10&stato=1: difficili da leggere, da ricordare e da salvare tra i preferiti. Con Spring Boot gli indirizzi si possono tenere puliti, scegliendo con cura dove mettere ogni dato. Prima di entrare nei dettagli, un colpo d'occhio sui due indirizzi che costruiremo:
1. Path variable: il dato dentro il percorso
Il modo più immediato è scrivere il dato direttamente nell'indirizzo. Immagina uno sportello di accoglienza che saluta chi entra:
/pazienti/benvenutorestituisce un saluto fisso/pazienti/benvenuto/Mariorestituisce "Benvenuto, Mario"/pazienti/benvenuto/Mario/Rossirestituisce "Benvenuto, Mario Rossi"
Nel codice il pezzo variabile si indica tra parentesi graffe ({nome}) e si recupera con @PathVariable. Ogni parte separata da / si chiama segmento, e qui c'è un dettaglio da notare: Spring guarda quanti segmenti hai scritto per scegliere il metodo giusto. Per questo i tre indirizzi qui sopra convivono senza pestarsi i piedi. Nel controller sono tre metodi, ciascuno con il suo percorso:
@GetMapping("benvenuto")
public String benvenuto() {
return "Benvenuto";
}
@GetMapping("benvenuto/{nome}")
public String benvenutoNome(@PathVariable String nome) {
return "Benvenuto, " + nome;
}
@GetMapping("benvenuto/{nome}/{cognome}")
public String benvenutoCompleto(@PathVariable String nome, @PathVariable String cognome) {
return "Benvenuto, " + nome + " " + cognome;
}E nel browser, aprendo l'ultimo indirizzo:

2. Query string: il dato dopo il punto interrogativo
Il secondo modo sfrutta una caratteristica dell'HTTP: dopo un ? puoi aggiungere coppie chiave=valore, separate da &:
/pazienti/accoglienza?nome=Mario&cognome=Rossi&eta=10
Nel codice ogni valore si recupera con @RequestParam, indicando il nome della chiave. Potresti metterne anche dieci. L'indirizzo resta lo stesso, cambiano solo i valori che gli passi. Un dettaglio utile: tutto ciò che viaggia nell'indirizzo è testo, e Spring lo converte nel tipo che hai dichiarato. Se eta è un numero e tu scrivi dieci, la conversione fallisce e il server risponde con un errore sulla richiesta. Il metodo che riceve i tre valori è questo:
@GetMapping("accoglienza")
public String accoglienza(@RequestParam String nome,
@RequestParam String cognome,
@RequestParam int eta) {
return "Benvenuto, " + nome + " " + cognome + ", " + eta + " anni";
}Con l'indirizzo che abbiamo visto, la risposta è questa:

Se invece al posto di 10 scrivi dieci, il numero non si riesce a ricavare e il server risponde con un 400 Bad Request, cioè "la richiesta è sbagliata":

Stessa risposta se dimentichi un parametro obbligatorio, per esempio eta.
Un parametro può anche essere facoltativo: basta dichiararlo con required = false. È il caso tipico di una ricerca, dove il codice è obbligatorio e il reparto no. Nel progetto di prova, su un controller dedicato alla ricerca (mappato su /ricerca), il metodo è questo:
@GetMapping("pazienti")
public String cercaPazienti(@RequestParam String codice,
@RequestParam(required = false) String reparto) {
return "Ricerca pazienti con codice " + codice
+ (reparto == null ? " in tutti i reparti" : " nel reparto " + reparto);
}GET /ricerca/pazienti?codice=rosso Ricerca pazienti con codice rosso in tutti i reparti GET /ricerca/pazienti?codice=rosso&reparto=cardiologia Ricerca pazienti con codice rosso nel reparto cardiologia GET /ricerca/pazienti 400 Bad Request (il codice è obbligatorio)
Quale scegliere?
C'è un terzo modo, il payload di una POST, che vedremo nel prossimo pezzo. Ecco i tre a confronto, con una regola pratica che aiuta a non pensarci troppo:
| Meccanismo | Annotazione | Come appare | Quando conviene |
|---|---|---|---|
| Path variable | @PathVariable | /pazienti/42 | Identificare una risorsa precisa |
| Query parameter | @RequestParam | /pazienti?codice=rosso | Filtri, paginazione, ordinamento |
| Request body | @RequestBody | JSON nel payload, invisibile nell'indirizzo | Creare o modificare oggetti complessi |
Se il percorso non corrisponde a nessun metodo, ottieni un 404. Se manca un parametro obbligatorio, il server risponde con un errore sulla richiesta.
Quando l'indirizzo non basta: torna il POST
Il limite dei due metodi precedenti è che tutto viaggia nell'indirizzo. Se i dati sono tanti, strutturati, o semplicemente non vuoi vederli nella barra del browser, serve il payload. Di come si manda e di cosa ci si mette dentro parliamo nel prossimo pezzo.
Due metodi, stesso indirizzo: quando litigano?
Che succede se scrivi due metodi che rispondono allo stesso indirizzo con lo stesso verbo? Che l'applicazione non parte. Spring ti segnala all'avvio che esiste già un metodo mappato su quel percorso, e non c'è modo di fargli scegliere. Meglio così: il problema salta fuori subito, e non quando il medico clicca. Il messaggio, copiato dal log (il secondo metodo, aggiunto per la prova, si chiamava sportelloDoppione), è questo:
Ambiguous mapping. Cannot map 'pazienteController' method
com.ospedale.prontosoccorso.controllers.PazienteController#sportelloDoppione()
to {GET [/pazienti/sportello]}: There is already 'pazienteController' bean method
com.ospedale.prontosoccorso.controllers.PazienteController#sportelloLettura() mapped.Se invece cambi il verbo, nessuno litiga. Nel controller sono due metodi, uno con @GetMapping e uno con @PostMapping, con lo stesso identico percorso:
@GetMapping("sportello")
public String sportelloLettura() {
return "Sportello in lettura (GET)";
}
@PostMapping("sportello")
public String sportelloScrittura() {
return "Sportello in scrittura (POST)";
}- GET
/pazienti/sportellorisponde "Sportello in lettura (GET)" - POST
/pazienti/sportellorisponde "Sportello in scrittura (POST)" - Un secondo GET
/pazienti/sportellomanda in errore l'avvio
Per Spring l'identità di un endpoint è la coppia verbo + indirizzo, non l'indirizzo da solo.
Quando due metodi sono "lo stesso metodo"?
Se hai un po' di Java alle spalle, conosci la regola della firma: in una classe non puoi avere due metodi con lo stesso nome, gli stessi parametri e lo stesso tipo di ritorno, perché il compilatore non saprebbe quale scegliere. Nel mondo web la regola è la stessa, ma gli ingredienti cambiano: a distinguere un endpoint dall'altro non sono il nome del metodo Java o i suoi parametri, sono il verbo e l'indirizzo.
Per questo puoi chiamare accogli un metodo e accogliDavvero un altro e avere comunque un problema, se entrambi rispondono a una GET su /pazienti/ciao. E per questo, al contrario, due metodi con lo stesso identico indirizzo possono convivere se uno risponde a GET e l'altro a POST.
Il browser sa fare solo GET: e ora?
C'è un problema pratico. Dalla barra degli indirizzi puoi generare solo richieste GET. Per provare una POST serve un client che ti lasci scegliere il verbo, e il più diffuso è Postman (gratuito): crei una nuova richiesta, scegli POST dal menu, scrivi l'indirizzo e premi Send. Se preferisci il terminale, curl fa la stessa cosa con una riga:
curl -X POST http://localhost:8080/pazienti/sportello
Sportello in scrittura (POST)
Sotto la risposta compare subito lo status code: 200 se tutto è andato bene, 404 Not Found se hai sbagliato indirizzo. Da qui in avanti lo useremo per provare tutto.
Conclusione
Il dato può viaggiare nell'indirizzo (in un segmento o dopo il punto interrogativo) oppure nel payload di una POST. Tre modi, ognuno con il suo uso, e una regola che li tiene insieme: per il server un endpoint è un verbo più un indirizzo.
Nel prossimo pezzo apriamo la busta: cosa succede quando dentro il payload ci mettiamo un paziente intero, e perché Spring sa tradurlo da solo?

Dario Amato
Sviluppatore software
Informatico con 15 anni di esperienza nello sviluppo software, guidato da una forte curiosità per le nuove frontiere dell'AI e del Web Marketing.
