3. Azioni: la risposta
Consideriamo l'architettura di un'applicazione Spring MVC:
![]() |
In questo capitolo, esaminiamo il processo che instrada la richiesta [1] al controller e all'azione [2a] che la elaborerà, un meccanismo noto come routing. Presentiamo inoltre le varie risposte [3] che un'azione può restituire al browser. Queste possono essere diverse da una vista V [4b].
3.1. Il nuovo progetto
Creiamo un nuovo progetto Spring MVC:
![]() |
- In [1-2], creiamo un nuovo progetto basato su Spring Boot;
![]() |
- in [3], il nome del progetto Maven;
- in [4], il gruppo Maven in cui verrà collocato l'output della compilazione del progetto;
- in [5], il nome assegnato all'output della compilazione;
- in [6], una descrizione del progetto;
- in [7], il pacchetto in cui verrà collocata la classe eseguibile del progetto;
- in [8], la natura del progetto. Si tratta di un progetto web con viste Thymeleaf. Qui vediamo tutte le dipendenze Maven pronte all'uso fornite dal progetto Spring Boot;
- in [9], specifichiamo che l'output della build Maven sarà impacchettato in un archivio JAR anziché in un WAR. Il progetto utilizzerà quindi un server Tomcat incorporato, che sarà incluso nelle sue dipendenze;
- in [10], si procede al passo successivo della procedura guidata;
- in [11], specifichiamo la directory del progetto;
![]() |
- in [12], il progetto generato;
- In [14-15], rinominare il pacchetto [istia.st.springmvc];
![]() |
- in [16], il nuovo nome del pacchetto;
- in [17], il nuovo progetto;
Ora creiamo una nuova classe;
![]() |
- nei punti [1-3], creiamo una nuova classe;
![]() |
- in [5] le diamo un nome e in [4] ne specifichiamo il pacchetto;
- in [6] il nuovo progetto;
La classe è attualmente la seguente:
package istia.st.springmvc;
public class ActionsController {
}
Stiamo aggiornando questo codice come segue:
package istia.st.springmvc;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ActionsController {
}
- Riga 6: L'annotazione [@RestController] indica due cose:
- che la classe [ActionsController] annotata in questo modo è un controller Spring MVC e, pertanto, contiene azioni che gestiscono gli URL dei client;
- che il risultato di queste azioni viene inviato al client;
L'altra annotazione [@Controller] che abbiamo incontrato è diversa: le azioni di un controller annotato in questo modo restituiscono il nome della vista che dovrebbe essere visualizzata. È quindi la combinazione di questa vista e del modello costruito dall'azione per questa vista che fornisce la risposta inviata al client.
Il cambiamento nella struttura del nostro progetto richiede una modifica nella configurazione del nostro progetto:
![]() |
La classe [Application] si evolve come segue:
package istia.st.springmvc.main;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan({"istia.st.springmvc.controllers"})
@EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- Riga 9: L'annotazione [ComponentScan] accetta come parametro un array di nomi di pacchetti in cui Spring Boot deve cercare i componenti Spring. Qui, includiamo il pacchetto [istia.st.springmvc.controllers] in questo array in modo che il controller annotato con [@RestController] possa essere trovato;
Creeremo varie azioni nel controller per illustrarne le caratteristiche principali. Innanzitutto, ci concentreremo sui vari tipi di risposte possibili per un'azione in un'applicazione senza viste.
3.2. [/a01, /a02] - Hello world
La nostra prima azione sarà la seguente:
@RestController
public class ActionsController {
// ----------------------- hello world ------------------------
@RequestMapping(value = "/a01", method = RequestMethod.GET)
public String a01() {
return "Greetings from Spring Boot!";
}
}
- Riga 4: L'annotazione [RequestMapping] specifica la richiesta gestita dall'azione annotata:
- l'attributo [value] è l'URL in fase di elaborazione,
- l'attributo [method] specifica il metodo accettato;
Pertanto, il metodo [a01] gestisce la richiesta HTTP [GET /a01].
- riga 5: il metodo [a01] restituisce un tipo [String], che verrà inviato così com'è al client;
- riga 6: la stringa restituita;
Eseguiamo l'applicazione come abbiamo già fatto diverse volte in precedenza, quindi utilizzando [Advanced Rest Client], richiediamo l'URL [/a01] con un GET [1-2]:
![]() |
- in [3], la risposta del server;
- in [4], le intestazioni HTTP della risposta. Possiamo vedere che la codifica utilizzata è [ISO-8859-1]. Potremmo preferire la codifica UTF-8. Questo può essere configurato;
- in [5], richiediamo lo stesso URL utilizzando il browser Chrome;
Aggiungiamo la seguente azione [/a02] all'[ActionsController] (a volte confonderemo l'URL con il metodo che lo gestisce, indicato come azione):
// ----------------------- accented characters - UTF8 ------------------------
@RequestMapping(value = "/a02", method = RequestMethod.GET, produces="text/plain;charset=UTF-8")
public String a02() {
return "caractères accentués : éèàôûî";
}
- Riga 2: L'attributo [produces="text/plain;charset=UTF-8"] indica che l'azione invia un flusso di testo con caratteri codificati in formato [UTF-8]. Questo formato consente specificatamente l'uso di caratteri accentati;
Per applicare questa nuova azione, è necessario riavviare l'applicazione:
![]() |
Il risultato è il seguente:
![]() |
- in [1], vediamo il tipo di documento inviato dal server;
- In [2-3], i caratteri accentati sono chiaramente visibili;
3.3. [/a03]: Restituisce un flusso XML
Aggiungiamo la seguente azione [/a03]:
// ----------------------- text/xml ------------------------
@RequestMapping(value = "/a03", method = RequestMethod.GET, produces = "text/xml;charset=UTF-8")
public String a03() {
String greeting = "<greetings><greeting>Greetings from Spring Boot!</greeting></greetings>";
return greeting;
}
- Riga 2: L'attributo [produces="text/xml;charset=UTF-8"] indica che l'azione invia un flusso XML con caratteri codificati in formato [UTF-8];
La sua esecuzione produce il seguente risultato:
![]() |
- In [1], l'intestazione HTTP specifica che il documento inviato è in formato HTML;
- in [2], il browser Chrome utilizza questa informazione per formattare il testo XML ricevuto;
Ricorda che con Chrome puoi visualizzare gli scambi HTTP tra il client e il server nella console degli sviluppatori (Ctrl-Shift-I):

D'ora in poi, non faremo sistematicamente screenshot degli scambi HTTP tra il client e il server. A volte, ci limiteremo a citare il testo di questi scambi.
3.4. [/a04, /a05]: restituzione di un feed JSON
Aggiungiamo la seguente azione [/a04]:
// ----------------------- produce from jSON ------------------------
@RequestMapping(value = "/a04", method = RequestMethod.GET)
public Map<String, Object> a04() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("1", "un");
map.put("2", new int[] { 4, 5 });
return map;
}
- Riga 3: L'azione restituisce un tipo [Map], ovvero un dizionario. Ricordiamo che con un controller [@RestController], il risultato dell'azione è la risposta inviata al client. Poiché HTTP è un protocollo per lo scambio di righe di testo, la risposta del client deve essere serializzata in una stringa. Per farlo, Spring MVC utilizza vari convertitori [Object <---> string]. L'associazione di un oggetto specifico con un convertitore avviene tramite configurazione. In questo caso, l'autoconfigurazione di Spring Boot esaminerà le dipendenze del progetto:
![]() |
Le dipendenze Jackson elencate sopra sono librerie per la serializzazione e la deserializzazione di oggetti in stringhe JSON. Spring Boot utilizzerà quindi queste librerie per serializzare e deserializzare gli oggetti restituiti dalle azioni. Un esempio di codice Java per la serializzazione e la deserializzazione di oggetti Java in JSON è disponibile nella Sezione 9.7.
Si noti alla riga 2 che non abbiamo specificato il tipo della risposta inviata. Vedremo quale sarà il tipo predefinito che verrà inviato.
I risultati in Chrome sono i seguenti [1-3]:
![]() |
Aggiungiamo ora la seguente azione [/a05]:
// ----------------------- produce from jSON - 2 ------------------------
@RequestMapping(value = "/a05", method = RequestMethod.GET)
public Personne a05() {
return new Personne(1,"carole",45);
}
La classe [Person] è la seguente:
![]() |
package istia.st.sprinmvc.models;
public class Personne {
// identifier
private Integer id;
// name
private String nom;
// age
private int age;
// manufacturers
public Personne() {
}
public Personne(String nom, int age) {
this.nom = nom;
this.age = age;
}
public Personne(Integer id, String nom, int age) {
this(nom, age);
this.id = id;
}
@Override
public String toString() {
return String.format("[id=%s, nom=%s, age=%d]", id, nom, age);
}
// getters and setters
...
}
L'esecuzione produce i seguenti risultati:
![]() |
- in [1], il server indica che il documento che sta inviando è in formato JSON;
- in [2], il documento JSON ricevuto;
3.5. [/a06]: restituisce uno stream vuoto
Aggiungiamo la seguente azione [/a06]:
// ----------------------- render an empty stream ------------------------
@RequestMapping(value = "/a06")
public void a06() {
}
- Alla riga 3, l'azione [/a06] non restituisce nulla. Spring MVC genererà quindi una risposta vuota al client;
L'esecuzione produce i seguenti risultati:
![]() |
Sopra, l'attributo HTTP [Content-Length] nella risposta indica che il server sta inviando un documento vuoto.
3.6. [/a07, /a08, /a09]: tipo di dati con [Content-Type]
Aggiungiamo la seguente azione [/a07]:
// ----------------------- text/html ------------------------
@RequestMapping(value = "/a07", method = RequestMethod.GET, produces = "text/html;charset=UTF-8")
public String a07() {
String greeting = "<h1>Greetings from Spring Boot!</h1>";
return greeting;
}
- Riga 2: l'azione [/a07] restituisce un flusso HTML [text/html];
- riga 4: una stringa HTML;
L'esecuzione produce i seguenti risultati:
![]() |
- in [1], vediamo che Chrome ha interpretato il tag HTML <h1>, che visualizza il suo contenuto in caratteri grandi;
Ora facciamo la stessa cosa con la seguente azione [/a08]:
// ----------------------- result HTML in text/plain ------------------------
@RequestMapping(value = "/a08", method = RequestMethod.GET, produces = "text/plain;charset=UTF-8")
public String a08() {
String greeting = "<h1>Greetings from Spring Boot!</h1>";
return greeting;
}
- Riga 2: La risposta dell'azione è di tipo [text/plain];
I risultati sono i seguenti:
![]() |
- In [1], Chrome non ha interpretato il tag HTML <h1> perché il server gli ha comunicato che stava inviando un flusso [text/plain] [2];
Proviamo qualcosa di simile con la seguente azione [/a09]:
// ----------------------- result HTML in text/xml ------------------------
@RequestMapping(value = "/a09", method = RequestMethod.GET, produces = "text/xml;charset=UTF-8")
public String a09() {
String greeting = "<h1>Greetings from Spring Boot!</h1>";
return greeting;
}
- Riga 2: inviamo un flusso [text/xml];
I risultati sono i seguenti:
![]() |
- In [1], Chrome non ha interpretato il tag HTML <h1> perché il server gli ha comunicato che stava inviando un flusso [text/xml] [2]. Ha quindi trattato il tag <h1> come un tag XML;
Questi esempi evidenziano l'importanza dell'intestazione HTTP [Content-Type] nella risposta del server. Il browser utilizza questa intestazione per determinare come interpretare il documento che riceve;
3.7. [/a10, /a11, /a12]: reindirizzamento del client
Creiamo un nuovo controller [RedirectController]:
![]() |
Il codice per [RedirectController] sarà per ora il seguente:
package istia.st.springmvc.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class RedirectController {
}
- Riga 7: Utilizziamo l'annotazione [@Controller], il che significa che, per impostazione predefinita, il tipo [String] del risultato dell'azione ora si riferisce al nome di un'azione o di una vista;
Creiamo la seguente azione [/a10]:
// ------------ bridge to third-party action -----------------------
@RequestMapping(value = "/a10", method = RequestMethod.GET)
public String a10() {
return "a01";
}
- Riga 4: restituiamo 'a01' come risultato, che è il nome di un'azione. Questa azione invierà quindi la risposta al client;
Ecco un esempio:
![]() |
- In [2], abbiamo ricevuto lo stream dall'azione [/a01];
- in [3], il browser visualizza l'URL dell'azione [/a10];
Ora creiamo la seguente azione [/a11]:
// ------------ temporary 302 redirect to a third-party action -----------------------
@RequestMapping(value = "/a11", method = RequestMethod.GET)
public String a11() {
return "redirect:/a01";
}
Otteniamo i seguenti risultati:
![]() |
- Nei log di Chrome [1-2], vediamo due richieste, una a [/a11] e l'altra a [/a01];
- in [3], il server risponde con un codice di stato [302], che indica al browser client di reindirizzarsi all'URL specificato dall'intestazione HTTP [Location:] [4]. Il codice di stato [302] è un codice di reindirizzamento temporaneo;
Il browser effettua quindi la seconda richiesta all'URL di reindirizzamento:
![]() |
- in [5], la seconda richiesta del client;
- in [6], il browser del client visualizza l'URL della richiesta di reindirizzamento;
Potresti voler indicare un reindirizzamento permanente; in tal caso, devi inviare al client il seguente header HTTP:
il che significa che il reindirizzamento è permanente. Questa differenza tra un reindirizzamento temporaneo (302) e uno permanente (301) viene presa in considerazione da alcuni motori di ricerca.
Scriviamo l'azione [/a12] che eseguirà questo reindirizzamento permanente:
// ------------ permanent 301 redirect to a third-party action----------------
@RequestMapping(value = "/a12", method = RequestMethod.GET)
public void a12(HttpServletResponse response) {
response.setStatus(301);
response.addHeader("Location", "/a01");
}
- Riga 3: chiediamo a Spring MVC di iniettare l'oggetto [HttpServletResponse], che incapsula la risposta inviata al client;
- riga 4: impostiamo lo [status] della risposta, l'intestazione HTTP [301]:
- riga 5: creiamo manualmente la seguente intestazione HTTP:
che è l'URL di reindirizzamento.
L'esecuzione produce i seguenti risultati:
![]() | ![]() |
Da questo esempio impareremo come:
- generare lo stato della risposta HTTP;
- includere un'intestazione HTTP nella risposta;
3.8. [/a13]: generare la risposta completa
È possibile controllare completamente la risposta, come mostra la seguente azione nella classe [ResponsesController]:
![]() |
// ----------------------- complete response generation ------------------------
@RequestMapping(value = "/a13")
public void a13(HttpServletResponse response) throws IOException {
response.setStatus(666);
response.addHeader("header1", "qq chose");
response.addHeader("Content-Type", "text/html;charset=UTF-8");
String greeting = "<h1>Greetings from Spring Boot!</h1>";
response.getWriter().write(greeting);
}
- Riga 3: Il risultato dell'azione è [void]. In questo caso, per inviare una risposta non vuota al client, è necessario utilizzare l'oggetto [HttpServletResponse response] fornito da Spring MVC;
- riga 4: assegniamo alla risposta uno stato che non verrà riconosciuto dal client;
- riga 5: aggiungiamo un'intestazione HTTP che non verrà riconosciuta dal client;
- riga 6: aggiungiamo un'intestazione HTTP [Content-Type] per specificare il tipo di dati che stiamo inviando, in questo caso HTML;
- Righe 7–8: il documento che segue le intestazioni HTTP nella risposta;
I risultati sono i seguenti:
![]() |
- in [1], riconosciamo gli elementi della nostra risposta;
- in [2-3], vediamo che Chrome ha ignorato il fatto che:
- lo stato HTTP della risposta non era uno stato HTTP riconosciuto,
- che l'intestazione [header1] non era un'intestazione HTTP riconosciuta;
Se il client non è un browser ma un client programmatico, sei libero di utilizzare qualsiasi codice di stato e intestazione desideri.



























