3. Ações: a resposta
Consideremos a arquitetura de uma aplicação Spring MVC:
![]() |
Neste capítulo, analisamos o processo que leva a solicitação [1] ao controlador e à ação [2a] que irão processá-la, um mecanismo conhecido como roteamento. Além disso, apresentamos as diferentes respostas [3] que uma ação pode enviar ao navegador. Pode 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 do Maven no qual será colocado o resultado da compilação do projeto;
- em [5], o nome dado ao produto da compilação;
- em [6], uma descrição do projeto;
- em [7], o pacote no qual será colocada a classe executável do projeto;
- em [8], a natureza do projeto. Trata-se de um projeto web com visualizações Thymeleaf. Observa-se aqui todas as dependências do Maven prontas para uso oferecidas pelo projeto Spring Boot;
- em [9], indica-se que o produto resultante da compilação do Maven será empacotado em um arquivo jar e não war. O projeto utilizará, então, um servidor Tomcat embutido que estará entre suas dependências;
- em [10], seguimos adiante no assistente;
- em [11], especifica-se a pasta do projeto;
![]() |
- em [12], o projeto gerado;
- em [14-15], renomeia-se o pacote [istia.st.springmvc];
![]() |
- em [16], o novo nome do pacote;
- em [17], o novo projeto;
Agora criamos uma nova classe;
![]() |
- em [1-3], criamos uma nova classe;
![]() |
- em [5], atribuímos a ela e, em [4], especificamos seu pacote;
- em [6], o novo projeto;
A classe, por enquanto, é a seguinte:
package istia.st.springmvc;
public class ActionsController {
}
Evoluímos esse código da seguinte maneira:
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 processam os URL dos clientes;
- 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 retornam o nome da visualização que deve ser exibida. É, então, a combinação dessa visualização com o modelo construído pela ação para essa visualização que fornece a resposta enviada ao cliente.
A mudança na estrutura do nosso projeto implica uma alteração na configuração do projeto:
![]() |
A classe [Application] sofre as seguintes alterações:
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] aceita como parâmetro uma matriz de nomes de pacotes onde o Spring Boot deve procurar componentes Spring. Aqui, colocamos nessa matriz o pacote [istia.st.springmvc.controllers] para que o controlador anotado por [@RestController] seja encontrado;
Vamos criar várias ações no controlador para ilustrar suas principais características. Primeiramente, vamos nos concentrar nos diversos tipos de respostas possíveis de uma ação em um aplicativo sem visualizações.
3.2. [/a01, /a02] - Hello world
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] qualifica a solicitação processada pela ação anotada:
- o atributo [value] é a solicitação URL processada,
- o atributo [method] define o método aceito;
Assim, o método [a01] processa a solicitação HTTP [GET /a01].
- linha 5: o método [a01] retorna um tipo [String], que será enviado tal como está ao cliente;
- linha 6: a string retornada;
Vamos executar o aplicativo como já fizemos várias vezes e, em seguida, com o cliente [Advanced Rest Client], solicitamos o URL [/a01] com um GET [1-2]:
![]() |
- em [3], a resposta do servidor;
- em [4], os cabeçalhos HTTP da resposta. Vemos que a codificação utilizada é [ISO-8859-1]. Podemos preferir a codificação UTF-8. Isso pode ser configurado;
- em [5], solicitamos o mesmo URL com o navegador Chrome;
Adicionamos a seguinte ação [/a02] no controlador [ActionsController] (assim, às vezes confundiremos o URL com o método que o processa sob o nome de ação):
// ----------------------- caracteres acentuados - 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]. Esse formato permite, entre outras coisas, o uso de caracteres acentuados;
Para que essa nova ação seja reconhecida, precisamos reiniciar o aplicativo:
![]() |
O resultado é o seguinte:
![]() |
- em [1], vemos a natureza do documento enviado pelo servidor;
- em [2-3], os caracteres acentuados aparecem corretamente;
3.3. [/a03]: gerar um fluxo XML
Adicionamos a ação [/a03] a seguir:
// ----------------------- 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];
Sua execução resulta no seguinte:
![]() |
- em [1], o cabeçalho HTTP especifica que o documento enviado é do tipo HTML;
- em [2], o navegador Chrome usa essa informação para formatar o texto XML recebido;
Vale lembrar que, no Chrome, é possível acessar as trocas de dados entre o cliente e o servidor na janela de desenvolvimento (Ctrl+Shift+I):

A partir de agora, não faremos capturas de tela sistemáticas das trocas HTTP entre o cliente e o servidor. Às vezes, nos limitaremos a indicar o texto dessas trocas.
3.4. [/a04, /a05]: gerar um fluxo jSON
Adicionamos a seguinte ação [/a04]:
// ----------------------- gerar 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. Lembramos que, com um controlador do tipo [@RestController], o resultado da ação é a resposta enviada ao cliente. Como o protocolo HTTP é um protocolo de troca de linhas de texto, a resposta do cliente deve ser serializada em uma cadeia de caracteres. Para isso, o Spring MVC utiliza diversos conversores [Objet <---> chaîne de caractères]. A associação de um objeto específico a um conversor é feita por meio de configuração. Aqui, a autoconfiguração do Spring Boot irá inspecionar as dependências do projeto:
![]() |
As dependências do Jackson acima são bibliotecas de serialização/desserialização de objetos em cadeias jSON. O Spring Boot utilizará, então, essas bibliotecas para serializar/desserializar os objetos retornados pelas ações. Um exemplo de código Java para serializar/deserializar objetos Java em jSON pode ser encontrado no parágrafo 9.7.
Observe-se, na linha 2, que não especificamos o tipo da resposta enviada. Veremos qual será o tipo padrão que será enviado.
Os resultados no Chrome [1-3] são os seguintes:
![]() |
Vamos agora adicionar a seguinte ação [/a05]:
// ----------------------- gerar jSON - 2 ------------------------
@RequestMapping(value = "/a05", method = RequestMethod.GET)
public Personne a05() {
return new Personne(1,"carole",45);
}
A classe [Personne] é a seguinte:
![]() |
package istia.st.sprinmvc.models;
public class Personne {
// identificador
private Integer id;
// nome
private String nom;
// idade
private int age;
// construtores
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 e setters
...
}
A execução produz os seguintes resultados:
![]() |
- em [1], o servidor indica que o documento que está enviando é o jSON;
- em [2], o documento jSON recebido;
3.5. [/a06]: retornar um fluxo vazio
Adicionamos a seguinte ação [/a06]:
// ----------------------- retornar um fluxo vazio ------------------------
@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 envia um documento vazio.
3.6. [/a07, /a08, /a09]: natureza do fluxo 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] gera um fluxo HTML [text/html];
- linha 4: uma sequência HTML;
A execução produz os seguintes resultados:
![]() |
- em [1], vemos que o Chrome interpretou a tag HTML <h1>, que exibe seu conteúdo em letras grandes;
Agora, vamos fazer o mesmo com a seguinte ação [/a08]:
// ----------------------- resultado de HTML em 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 informou que estava enviando um fluxo [text/plain] [2];
Vamos tentar novamente algo semelhante com a ação [/a09] a seguir:
// ----------------------- resultado de HTML em 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 do tipo [text/xml];
Os resultados são os seguintes:
![]() |
- no [1], o Chrome não interpretou a tag HTML <h1> porque o servidor informou que estava enviando um fluxo [text/xml] [2]. Ele, então, tratou a tag <h1> como uma tag XML;
Esses exemplos destacam a importância do cabeçalho HTTP [Content-Type] na resposta do servidor. O navegador utiliza esse cabeçalho para saber como interpretar o documento que recebe;
3.7. [/a10, /a11, /a12]: redirecionar o cliente
Criamos um novo controlador [RedirectController]:
![]() |
O código de [RedirectCntroller] 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 faz com que, a partir de agora, por padrão, o tipo [String] do resultado das ações indique o nome de uma ação ou de uma visualização;
Criamos a seguinte ação [/a10]:
// ------------ redirecionamento para uma ação de terceiros -----------------------
@RequestMapping(value = "/a10", method = RequestMethod.GET)
public String a10() {
return "a01";
}
- linha 4: retornamos como resultado 'a01', que é o nome de uma ação. Será ela, então, que enviará a resposta ao cliente;
Veja um exemplo:
![]() |
- em [2], recebemos o fluxo da ação [/a01];
- no [3], o navegador exibe o URL da ação [/a10];
Agora, criamos a seguinte ação [/a11]:
// ------------ redirecionamento temporário 302 para uma ação de terceiros -----------------------
@RequestMapping(value = "/a11", method = RequestMethod.GET)
public String a11() {
return "redirect:/a01";
}
Obtemos os seguintes resultados:
![]() |
- nos logs do Chrome [1-2], vemos duas solicitações: uma para [/a11] e outra para [/a01];
- em [3], o servidor responde com um código [302] que solicita que o navegador cliente seja redirecionado para oURL indicado pelo cabeçalho HTTP [Location:] [4]. O código [302] é um código de redirecionamento temporário;
O navegador então faz a segunda solicitação para o código de redirecionamento URL:
![]() |
- em [5], a segunda solicitação do cliente;
- para [6], o navegador do cliente exibe o URL da solicitação de redirecionamento;
Pode-se querer indicar um redirecionamento permanente; nesse caso, é necessário enviar ao cliente o cabeçalho HTTP a seguir:
o que significa que o redirecionamento é permanente. Essa diferença entre redirecionamento temporário (302) e permanente (301) é levada em consideração por alguns mecanismos de busca.
Escrevemos a ação [/a12], que realizará esse redirecionamento permanente:
// ------------ redirecionamento permanente 301 para uma ação de terceiros----------------
@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: define-se o [status] da resposta, o [301] do cabeçalho HTTP:
- linha 5: cria-se manualmente o cabeçalho HTTP a seguir:
que é o cabeçalho de redirecionamento URL.
A execução produz os seguintes resultados:
![]() | ![]() |
Deste exemplo, destaca-se a maneira de:
- gerar o status da resposta HTTP;
- incluir um cabeçalho HTTP na resposta;
3.8. [/a13]: gerar a resposta completa
É possível controlar totalmente a resposta, conforme mostra a ação a seguir da classe [ResponsesController]:
![]() |
// ----------------------- geração completa da resposta ------------------------
@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]. Nesse caso, para enviar uma resposta não vazia ao cliente, é preciso usar o objeto [HttpServletResponse response] fornecido pelo Spring MVC;
- linha 4: atribui-se à resposta um status que não será reconhecido pelo cliente;
- linha 5: adiciona-se um cabeçalho HTTP que não será reconhecido pelo cliente;
- linha 6: adiciona-se um cabeçalho HTTP [Content-Type] para especificar o tipo de fluxo que será enviado, neste caso, HTML;
- linhas 7-8: o documento que seguirá os cabeçalhos HTTP na resposta;
Os resultados são os seguintes:
![]() |
- em [1], reconhecemos os elementos da nossa resposta;
- em [2-3], percebe-se que o Chrome ignorou o fato de que:
- o status HTTP da resposta não era um status HTTP reconhecido,
- que o cabeçalho [header1] não era um cabeçalho HTTP reconhecido;
Se o cliente não for um navegador, mas um cliente programado, é possível usar os status e cabeçalhos que se desejar.



























