3. Ações: A Resposta
Vamos considerar a arquitetura de uma aplicação Spring MVC:
![]() |
Neste capítulo, examinamos o processo que encaminha a solicitação [1] para o controlador e a ação [2a] que a processará, um mecanismo conhecido como roteamento. Apresentamos também as várias respostas [3] que uma ação pode devolver ao navegador. Estas podem ser algo diferente de uma visualização V [4b].
3.1. O novo projeto
Criamos um novo projeto Spring MVC:
![]() |
- Em [1-2], criamos um novo projeto baseado no Spring Boot;
![]() |
- em [3], o nome do projeto Maven;
- em [4], o grupo Maven onde será colocada a saída da compilação do projeto;
- em [5], o nome atribuído ao resultado da compilação;
- em [6], uma descrição do projeto;
- em [7], o pacote onde a classe executável do projeto será colocada;
- em [8], a natureza do projeto. Trata-se de um projeto web com vistas Thymeleaf. Aqui, vemos todas as dependências Maven prontas a usar fornecidas pelo projeto Spring Boot;
- em [9], especificamos que o resultado da compilação do Maven será empacotado num arquivo JAR em vez de um WAR. O projeto utilizará então um servidor Tomcat incorporado, que será incluído nas suas dependências;
- em [10], avançamos para o próximo passo do assistente;
- em [11], especifique o diretório do projeto;
![]() |
- em [12], o projeto gerado;
- Em [14-15], renomeie o pacote [istia.st.springmvc];
![]() |
- em [16], o novo nome do pacote;
- em [17], o novo projeto;
Criamos agora uma nova classe;
![]() |
- em [1-3], criamos uma nova classe;
![]() |
- em [5] atribuímos-lhe um nome e, em [4], especificamos o seu pacote;
- em [6] o novo projeto;
A classe está atualmente assim:
package istia.st.springmvc;
public class ActionsController {
}
Estamos a atualizar este código da seguinte forma:
package istia.st.springmvc;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ActionsController {
}
- Linha 6: A anotação [@RestController] indica duas coisas:
- que a classe [ActionsController] assim anotada é um controlador Spring MVC e, portanto, contém ações que tratam de URLs do cliente;
- que o resultado dessas ações é enviado ao cliente;
A outra anotação [@Controller] que encontramos é diferente: as ações de um controlador assim anotado devolvem o nome da vista que deve ser apresentada. É então a combinação desta vista e do modelo construído pela ação para esta vista que fornece a resposta enviada ao cliente.
A alteração na estrutura do nosso projeto requer uma alteração na configuração do nosso projeto:
![]() |
A classe [Application] evolui da seguinte forma:
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);
}
}
- Linha 9: A anotação [ComponentScan] recebe como parâmetro uma matriz de nomes de pacotes onde o Spring Boot deve procurar componentes Spring. Aqui, incluímos o pacote [istia.st.springmvc.controllers] nesta matriz para que o controlador anotado com [@RestController] possa ser encontrado;
Iremos criar várias ações no controlador para ilustrar as suas principais funcionalidades. Primeiro, iremos concentrar-nos nos vários tipos de respostas possíveis para uma ação numa aplicação sem vistas.
3.2. [/a01, /a02] - Hello world
A nossa primeira ação será a seguinte:
@RestController
public class ActionsController {
// ----------------------- hello world ------------------------
@RequestMapping(value = "/a01", method = RequestMethod.GET)
public String a01() {
return "Greetings from Spring Boot!";
}
}
- Linha 4: A anotação [RequestMapping] especifica a solicitação tratada pela ação anotada:
- o atributo [value] é a URL que está a ser processada,
- o atributo [method] especifica o método aceite;
Assim, o método [a01] trata a solicitação HTTP [GET /a01].
- linha 5: o método [a01] retorna um tipo [String], que será enviado tal como está para o cliente;
- linha 6: a string devolvida;
Vamos executar a aplicação como já fizemos várias vezes antes e, em seguida, utilizando o [Advanced Rest Client], solicitamos a URL [/a01] com um GET [1-2]:
![]() |
- em [3], a resposta do servidor;
- em [4], os cabeçalhos HTTP da resposta. Podemos ver que a codificação utilizada é [ISO-8859-1]. Talvez prefiramos a codificação UTF-8. Isto pode ser configurado;
- em [5], solicitamos o mesmo URL utilizando o navegador Chrome;
Adicionamos a seguinte ação [/a02] ao [ActionsController] (por vezes, confundimos a URL com o método que a processa, referido como a ação):
// ----------------------- accented characters - UTF8 ------------------------
@RequestMapping(value = "/a02", method = RequestMethod.GET, produces="text/plain;charset=UTF-8")
public String a02() {
return "caractères accentués : éèàôûî";
}
- Linha 2: O atributo [produces="text/plain;charset=UTF-8"] indica que a ação envia um fluxo de texto com caracteres codificados no formato [UTF-8]. Este formato permite especificamente a utilização de caracteres acentuados;
Para aplicar esta nova ação, temos de reiniciar a aplicação:
![]() |
O resultado é o seguinte:
![]() |
- em [1], vemos o tipo de documento enviado pelo servidor;
- Em [2-3], os caracteres acentuados são claramente visíveis;
3.3. [/a03]: Devolver um fluxo XML
Adicionamos a seguinte ação [/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;
}
- Linha 2: O atributo [produces="text/xml;charset=UTF-8"] indica que a ação envia um fluxo XML com caracteres codificados no formato [UTF-8];
A sua execução produz o seguinte:
![]() |
- Em [1], o cabeçalho HTTP especifica que o documento enviado é HTML;
- em [2], o navegador Chrome utiliza esta informação para formatar o texto XML recebido;
Lembre-se de que, no Chrome, pode visualizar as trocas HTTP entre o cliente e o servidor na consola do programador (Ctrl-Shift-I):

A partir de agora, não iremos sistematicamente capturar imagens das trocas HTTP entre o cliente e o servidor. Por vezes, limitar-nos-emos a citar o texto dessas trocas.
3.4. [/a04, /a05]: devolução de um feed JSON
Adicionamos a seguinte ação [/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;
}
- Linha 3: A ação retorna um tipo [Map], um dicionário. Recorde-se que, com um controlador [@RestController], o resultado da ação é a resposta enviada ao cliente. Como o HTTP é um protocolo para a troca de linhas de texto, a resposta do cliente deve ser serializada numa string. Para isso, o Spring MVC utiliza vários conversores [Object <---> string]. A associação de um objeto específico a um conversor é feita através da configuração. Aqui, a configuração automática do Spring Boot irá inspecionar as dependências do projeto:
![]() |
As dependências do Jackson listadas acima são bibliotecas para serializar e deserializar objetos em cadeias de caracteres JSON. O Spring Boot utilizará então estas bibliotecas para serializar e deserializar os objetos devolvidos pelas ações. Um exemplo de código Java para serializar e deserializar objetos Java em JSON pode ser encontrado na Secção 9.7.
Repare que, na linha 2, não especificámos o tipo da resposta a ser enviada. Veremos qual o tipo predefinido que será enviado.
Os resultados no Chrome são os seguintes [1-3]:
![]() |
Vamos agora adicionar a seguinte ação [/a05]:
// ----------------------- produce from jSON - 2 ------------------------
@RequestMapping(value = "/a05", method = RequestMethod.GET)
public Personne a05() {
return new Personne(1,"carole",45);
}
A classe [Person] é a seguinte:
![]() |
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
...
}
A execução produz os seguintes resultados:
![]() |
- em [1], o servidor indica que o documento que está a enviar é JSON;
- em [2], o documento JSON recebido;
3.5. [/a06]: devolver um fluxo vazio
Adicionamos a seguinte ação [/a06]:
// ----------------------- render an empty stream ------------------------
@RequestMapping(value = "/a06")
public void a06() {
}
- Na linha 3, a ação [/a06] não retorna nada. O Spring MVC irá então gerar uma resposta vazia para o cliente;
A execução produz os seguintes resultados:
![]() |
Acima, o atributo HTTP [Content-Length] na resposta indica que o servidor está a enviar um documento vazio.
3.6. [/a07, /a08, /a09]: tipo de dados com [Content-Type]
Adicionamos a seguinte ação [/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;
}
- Linha 2: A ação [/a07] devolve um fluxo HTML [text/html];
- linha 4: uma cadeia de caracteres HTML;
A execução produz os seguintes resultados:
![]() |
- em [1], vemos que o Chrome interpretou a tag HTML <h1>, que exibe o seu conteúdo em letras grandes;
Agora, vamos fazer o mesmo com a seguinte ação [/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;
}
- Linha 2: A resposta da ação é do tipo [text/plain];
Os resultados são os seguintes:
![]() |
- Em [1], o Chrome não interpretou a tag HTML <h1> porque o servidor indicou que estava a enviar um fluxo [text/plain] [2];
Vamos tentar algo semelhante com a seguinte ação [/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;
}
- Linha 2: Enviamos um fluxo [text/xml];
Os resultados são os seguintes:
![]() |
- Em [1], o Chrome não interpretou a tag HTML <h1> porque o servidor indicou que estava a enviar um fluxo [text/xml] [2]. Em seguida, tratou a tag <h1> como uma tag XML;
Estes exemplos destacam a importância do cabeçalho HTTP [Content-Type] na resposta do servidor. O navegador utiliza este cabeçalho para determinar como interpretar o documento que recebe;
3.7. [/a10, /a11, /a12]: redirecionar o cliente
Criamos um novo controlador [RedirectController]:
![]() |
O código para o [RedirectController] será, por enquanto, o seguinte:
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 {
}
- Linha 7: Utilizamos a anotação [@Controller], o que significa que, por predefinição, o tipo [String] do resultado da ação refere-se agora ao nome de uma ação ou de uma vista;
Criamos a seguinte ação [/a10]:
// ------------ bridge to third-party action -----------------------
@RequestMapping(value = "/a10", method = RequestMethod.GET)
public String a10() {
return "a01";
}
- Linha 4: Devolvemos «a01» como resultado, que é o nome de uma ação. Esta ação enviará então a resposta ao cliente;
Aqui está um exemplo:
![]() |
- Em [2], recebemos o fluxo da ação [/a01];
- em [3], o navegador exibe o URL da ação [/a10];
Criamos agora a seguinte ação [/a11]:
// ------------ temporary 302 redirect to a third-party action -----------------------
@RequestMapping(value = "/a11", method = RequestMethod.GET)
public String a11() {
return "redirect:/a01";
}
Obtemos os seguintes resultados:
![]() |
- Nos registos do Chrome [1-2], vemos duas solicitações, uma para [/a11] e outra para [/a01];
- em [3], o servidor responde com um código de estado [302], que instrui o navegador do cliente a redirecionar para o URL especificado pelo cabeçalho HTTP [Location:] [4]. O código de estado [302] é um código de redirecionamento temporário;
O navegador faz então a segunda solicitação para a URL de redirecionamento:
![]() |
- em [5], a segunda solicitação do cliente;
- em [6], o navegador do cliente exibe o URL da solicitação de redirecionamento;
Pode querer indicar um redirecionamento permanente; nesse caso, deve enviar o seguinte cabeçalho HTTP ao cliente:
o que significa que o redirecionamento é permanente. Esta diferença entre um redirecionamento temporário (302) e um redirecionamento permanente (301) é tida em conta por alguns motores de busca.
Escrevemos a ação [/a12] que irá realizar este redirecionamento 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");
}
- Linha 3: Solicitamos ao Spring MVC que injete o objeto [HttpServletResponse], que encapsula a resposta enviada ao cliente;
- linha 4: definimos o [status] da resposta, o cabeçalho HTTP [301]:
- linha 5: criamos manualmente o seguinte cabeçalho HTTP:
que é o URL de redirecionamento.
A execução produz os seguintes resultados:
![]() | ![]() |
A partir deste exemplo, vamos aprender a:
- gerar o estado da resposta HTTP;
- incluir um cabeçalho HTTP na resposta;
3.8. [/a13]: gerar a resposta completa
É possível controlar totalmente a resposta, como demonstrado pela seguinte ação na 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);
}
- Linha 3: O resultado da ação é [void]. Neste caso, para enviar uma resposta não vazia ao cliente, deve utilizar o objeto [HttpServletResponse response] fornecido pelo Spring MVC;
- linha 4: atribuímos um estado à resposta que não será reconhecido pelo cliente;
- linha 5: adicionamos um cabeçalho HTTP que não será reconhecido pelo cliente;
- linha 6: adicionamos um cabeçalho HTTP [Content-Type] para especificar o tipo de dados que estamos a enviar, neste caso HTML;
- Linhas 7–8: O documento que se segue aos cabeçalhos HTTP na resposta;
Os resultados são os seguintes:
![]() |
- em [1], reconhecemos os elementos da nossa resposta;
- em [2-3], vemos que o Chrome ignorou o facto de que:
- o estado HTTP da resposta não era um estado HTTP reconhecido,
- que o cabeçalho [header1] não era um cabeçalho HTTP reconhecido;
Se o cliente não for um navegador, mas sim um cliente programático, pode utilizar os códigos de estado e cabeçalhos que desejar.



























