Skip to content

10. Aplicativo web MVC [personne] – versão 5

10.1. Introduction

Nesta versão, fizemos duas alterações:

A primeira diz respeito à forma como o cliente indica ao servidor a ação que deseja realizar. Até agora, essa ação era especificada por meio de um parâmetro chamado [action] na solicitação do GET ou do POST do cliente. Nesse caso, a ação será especificada pelo último elemento da URL solicitada pelo cliente, conforme mostra a sequência a seguir:

Image

Em [1], a URL para a qual o formulário foi enviado é [/personne5/do/validationFormulaire]. Foi o último elemento, [validationFormulaire], da URL que permitiu ao controlador reconhecer a ação a ser realizada. Em [2], o POST, gerado pelo link [Retour au formulaire], foi direcionado para a URL [/personne5/do/retourFormulaire]. Mais uma vez, o último elemento [retourFormulaire] da URL indica ao controlador a ação a ser executada.

Estamos introduzindo essa modificação porque esse é o método utilizado pelos frameworks de desenvolvimento web mais difundidos, como o Struts ou o Spring MVC.

Todas as URLs do aplicativo terão o formato [/personne5/do/action]. O arquivo [web.xml] do aplicativo [/personne5] indicará que este aceita URLs no formato [/do/*]:


    <servlet-mapping>
        <servlet-name>personne</servlet-name>
        <url-pattern>/do/*</url-pattern>
</servlet-mapping>

O controlador recuperará o nome da ação a ser executada da seguinte maneira:

         // recuperando a ação a ser executada
String action=request.getPathInfo();

O método [getPathInfo] do objeto [request] retorna o último elemento da URL da solicitação.

A segunda modificação diz respeito à forma de armazenar as entradas feitas pelo usuário entre dois ciclos de solicitação/resposta. Atualmente, essas informações são armazenadas em uma sessão. Esse método pode apresentar desvantagens se houver muitos usuários e muitos dados a serem armazenados para cada um deles. De fato, cada usuário possui sua própria sessão. Além disso, essa sessão permanece ativa por algum tempo após a saída de um usuário, a menos que se tenha o cuidado de oferecer a ele uma opção de logout. Assim, 1.000 sessões de 1.000 bytes ocuparão 1 MB de memória. Essa ainda é uma exigência moderada, e há poucas aplicações que tenham 1.000 sessões ativas simultaneamente.

No entanto, existem alternativas à sessão que consomem menos memória, e é bom conhecê-las. Usaremos aqui o método dos cookies. Vamos ilustrar isso com um exemplo.


Etapa 1: o usuário envia um formulário:


Esse ciclo de solicitação/resposta gera as seguintes trocas HTTP entre o cliente e o servidor:

[1] : [demande du client]

POST /personne5/do/validationFormulaire HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0,5
Accept-Language: fr-fr,fr;q=0.8,en;q=0.6,en-us;q=0.4,de;q=0.2
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/pessoa5/do/formulário
Cookie: JSESSIONID=6C6F4D112803A7E3696D41F5750CEDE7
Content-Type: application/x-www-form-urlencoded
Content-Length: 24

txtNom=pauline&txtAge=18

Trata-se de um POST clássico. Não há nada de especial a ser destacado aqui, exceto que, embora não se vá utilizar uma sessão, o servidor web cria uma mesmo assim. Isso fica evidente no token de sessão que o navegador retorna ao servidor na linha 11 e que havia recebido anteriormente do servidor.

[2]: [réponse du serveur]

1
2
3
4
5
6
7
HTTP/1.x 200 OK
Server: Apache-Coyote/1.1
Set-Cookie: nom=pauline
Set-Cookie: age=18
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 547
Date: Mon, 22 May 2006 08:03:51 GMT

Vemos que, nas linhas 3 e 4, os cabeçalhos HTTP e [Set-Cookie] foram enviados ao navegador do cliente: um para o nome (linha 3) e outro para a idade (linha 4). Os valores desses cookies são os valores enviados na linha 14 dos cabeçalhos POST e [1] acima.


Etapa 2: Retorno ao formulário


Image

Esse ciclo de solicitação/resposta resulta nas seguintes trocas de HTTP entre o cliente e o servidor:

[1]: [demande du client]

POST /personne5/do/retourFormulaire HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0,5
Accept-Language: fr-fr,fr;q=0.8,en;q=0.6,en-us;q=0.4,de;q=0.2
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/pessoa5/do/validationFormulaire
Cookie: nom=pauline; age=18; JSESSIONID=6C6F4D112803A7E3696D41F5750CEDE7
Content-Type: application/x-www-form-urlencoded
Content-Length: 0

Observamos aqui o POST, gerado pelo clique no link [Retour au formulaire]. Na linha 11, vemos que o navegador reenvia ao servidor os cookies que recebeu ([nom, age, JSESSIONID]) por meio do cabeçalho HTTP [Cookie]. Esse é o princípio dos cookies. O cliente reenvia ao servidor os cookies que este lhe enviou. Neste exemplo, o controlador receberá os valores [pauline, 18], que deverá inserir nos campos [txtNom, txtAge] da visualização [formulaire] exibida em [2].

[2]: [réponse du serveur]

1
2
3
4
5
HTTP/1.x 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 2341
Date: Mon, 22 May 2006 08:16:47 GMT

Não há nada de especial a ser destacado aqui, exceto o fato de que, nessa resposta, o servidor não enviou cookies. Isso não impedirá que o navegador reenvie, na próxima troca de dados, todos os cookies que recebeu do servidor, mesmo que isso não tenha utilidade. Assim, reduz-se a pressão sobre a memória disponível do servidor, em troca de um aumento no fluxo de caracteres nas trocas cliente/servidor.

10.2. O projeto Eclipse

Para criar o projeto Eclipse [mvc-personne-05] da aplicação web [/personne5], duplicaremos o projeto [mvc-personne-04] seguindo o procedimento descrito no parágrafo 6.2.

10.3. Configuração da aplicação web [personne5]

O arquivo web.xml da aplicação /personne5 é o seguinte:


<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
    xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <display-name>mvc-personne-05</display-name>
    <!--  ServletPersonne -->
    <servlet>
        <servlet-name>personne</servlet-name>
        <servlet-class>
            istia.st.servlets.personne.ServletPersonne
        </servlet-class>
...
    </servlet>
    <!--  Mapeamento ServletPersonne-->
    <servlet-mapping>
        <servlet-name>personne</servlet-name>
        <url-pattern>/do/*</url-pattern>
    </servlet-mapping>
    <!--  arquivos de página inicial -->
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

Este arquivo é idêntico ao da versão anterior, exceto por alguns detalhes:

  • linha 6: o nome de exibição da aplicação web mudou para [mvc-personne-05]
  • linha 18: as URLs processadas pelo aplicativo têm o formato [/do/*]. Anteriormente, apenas a URL [/main] era processada. Agora, há tantas URLs quanto ações a serem processadas.

A página inicial [index.jsp] muda:


<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<%@ taglib uri="/WEB-INF/c.tld" prefix="c" %>

<c:redirect url="/do/formulaire"/>
  • linha 5: a página [index.jsp] redireciona o cliente para a URL [/personne5/do/formulaire], o que equivale a solicitar ao controlador que execute a ação [formulaire].

10.4. O código das visualizações

As visualizações [formulaire, réponse, erreurs] sofrem poucas alterações. A única mudança decorre do fato de que a ação a ser executada não é mais especificada da mesma forma que antes, quando era definida em um campo oculto chamado [action] nos formulários enviados. Agora, ela é definida na URL de destino dos formulários enviados, c.a.d, no atributo [action] da tag <form>:

[formulaire.jsp]:


...
<html>
  <head>
    <title>Personne - formulaire</title>
    <script language="javascript">
...
    </script>
  </head>
  <body>
    <center>
      <h2>Personne - formulaire</h2>
      <hr>
      <form name="frmPersonne" action="validationFormulaire" method="post">
...
      </form>
    </center>
  </body>
</html>
  • linha [13]: o parâmetro [action] do formulário reaparece após ter desaparecido por algum tempo nas versões anteriores. Para entender o valor desse atributo aqui, é preciso lembrar que todas as URLs processadas pelo aplicativo têm o formato [/do/action]. Na linha [13], o atributo [action] tem como valor uma URL relativa (que não começa com /). Assim, o navegador irá completá-la com a URL da página atualmente exibida, ou seja, necessariamente uma URL no formato [/do/action]. O último elemento será substituído pela URL relativa do atributo [action] da tag <form>, resultando na URL [/do/validationFormulaire] como destino do POST.
  • O campo oculto [action] desapareceu

[réponse.jsp]:


...

<html>
...
  <body>
      ...
    <form name="frmPersonne" action="retourFormulaire" method="post">
    </form>
    <a href="javascript:document.frmPersonne.submit();">
      ${lienRetourFormulaire}
    </a>
  </body>
</html>

  • linha [7]: o destino do POST será [/do/retourFormulaire]
  • o campo oculto [action] desapareceu no formulário das linhas 7-8.

[erreurs.jsp]:


...
<html>
...
  <body>
...
    <form name="frmPersonne" action="retourFormulaire" method="post">
    </form>
    <a href="javascript:document.frmPersonne.submit();">
      ${lienRetourFormulaire}
    </a>
  </body>
</html>

  • linha [6]: o destino do POST será [/do/retourFormulaire]
  • o campo oculto [action] desapareceu no formulário das linhas 6-7.

Recomenda-se ao leitor que teste essas novas visualizações seguindo o princípio apresentado nas versões anteriores.

10.5. O controlador [ServletPersonne]

O controlador [ServletPersonne] do aplicativo web [/personne5] processará as seguintes ações:

n.º
solicitação
origem
processamento
1
[GET /personne5/do/formulaire]
URL digitada pelo usuário
- enviar a visualização [formulaire] vazia
2
[POST
/pessoa5/do/validationFormulaire]
com os parâmetros [txtNom, txtAge]
publicados
clicando no botão
[Envoyer] da visualização
[formulaire]
- verifique os valores dos parâmetros [txtNom, txtAge]
- se estiverem incorretos, enviar a visualização [erreurs(erreurs)]
- se estiverem corretos, enviar a visualização [reponse(nom,age)]
3
[POST
/pessoa5/fazer/retourFormulaire]
sem parâmetros enviados
clique no link [Voltar ao
formulário] nas visualizações
[réponse] e [erreurs].
- enviar a visualização [formulaire] pré-preenchida com os últimos valores inseridos

A estrutura do controlador [ServletPersonne] é idêntica à da versão anterior. Analisamos as alterações feitas nos métodos [doValidationFormulaire, doRetourFormulaire, doGet], já que os métodos [init, doInit, doPost] não sofreram alterações.

10.5.1. O método [doGet]

O método [doGet] não recupera a ação a ser executada da mesma forma que nas versões anteriores:

        @SuppressWarnings("unchecked")
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {

         // verifica-se como ocorreu a inicialização do servlet
        if (erreursInitialisation.size() != 0) {
...
        }
         // recupera-se o método de envio da solicitação
        String méthode=request.getMethod().toLowerCase();
         // recupera-se a ação a ser executada
        String action=request.getPathInfo();
         // ação?
        if(action==null){
            action="/formulaire";
        }
         // execução da ação
        if(méthode.equals("get") && action.equals("/formulaire")){
             // inicialização do aplicativo
            doInit(request,response);
            return;
        }
        if(méthode.equals("post") && action.equals("/validationFormulaire")){
             // validação do formulário de preenchimento
            doValidationFormulaire(request,response);
            return;
        }
        if(méthode.equals("post") && action.equals("/retourFormulaire")){
             // retorno ao formulário de preenchimento
            doRetourFormulaire(request,response);
            return;
        }
         // outros casos
        doInit(request,response);
    }
  • linha 12: recupera-se a ação a ser executada. Ela tem o formato [/action].
  • linhas 18-22: processamento da ação [/formulaire] solicitada por uma solicitação GET
  • linhas 23-27: processamento da ação [/validationFormulaire] solicitada por uma solicitação POST
  • linhas 28-32: processamento da ação [/retourFormulaire] solicitada por uma solicitação POST

10.5.2. O método [doValidationFormulaire]

Este método processa a solicitação nº 2 [POST /personne5/do/validationFormulaire] com [txtNom, txtAge] nos elementos postados. Seu código é o seguinte:

// validação do formulário
    void doValidationFormulaire(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException{
         // recuperação dos parâmetros
        String nom = request.getParameter("txtNom");
        String age = request.getParameter("txtAge");
         // que são armazenados em um cookie
        response.addCookie(new Cookie("nom",nom));
        response.addCookie(new Cookie("age",age));
         // verificação dos parâmetros
        ...
    }

Novidades:

  • o método [doValidationFormulaire] retorna, como resposta, uma das visualizações [réponse, erreurs]. Seja qual for essa resposta, o controlador insere nela dois cookies, linhas 8-9. Um cookie é representado por um objeto [Cookie], cujo construtor aceita dois parâmetros: a chave do cookie e o valor associado a ela.
  • linha 8: o valor inserido para o nome é colocado em um cookie com a chave “nom”
  • linha 9: o valor inserido para a idade é colocado em um cookie com a chave “age”
  • Um cookie é adicionado à resposta HTTP enviada ao cliente pelo método [response.addCookie]. Essa resposta é, neste momento, apenas preparada. Ela só será efetivamente enviada quando a página JSP da visualização for executada e enviada ao cliente.

10.5.3. O método [doRetourFormulaire]

Este método processa a solicitação nº 2 [POST /personne5/do/retourFormulaire] sem elementos enviados. Seu código é o seguinte:

         // exibição do formulário pré-preenchido
    void doRetourFormulaire(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
         // recuperam-se os cookies do usuário
        Cookie[] cookies=request.getCookies();
        String nom=null;
        String age=null;
        int nbCookies=0;
        for(int i=0;i<cookies.length && nbCookies<2;i++){
            if(cookies[i].getName().equals("nom")){
                nom=cookies[i].getValue();
                nbCookies++;
            }else{
                if(cookies[i].getName().equals("age")){
                    age=cookies[i].getValue();
                    nbCookies++;
                }
            }
        }
         // prepara-se o modelo do formulário
        request.setAttribute("nom",nom);
        request.setAttribute("age",age);
         // exibe o formulário
        getServletContext().getRequestDispatcher((String)params.get("urlFormulaire")).forward(
                request, response);
        return;
    }

Novidades:

O método [doRetourFormulaire] deve exibir um formulário pré-preenchido com as últimas entradas feitas. Na versão anterior, essas entradas estavam na sessão. Nesta versão, não se utiliza mais a sessão, mas sim cookies para memorizar elementos entre duas trocas cliente-servidor. Quando o cliente solicitou a validação do formulário, recebeu como resposta a visualização [réponse] ou [erreurs], conforme o caso, acompanhada de dois cookies denominados “nome” e “idade”. Ao clicar no link [Retour au formulaire] dessas duas visualizações, o que gera uma POST na URL [/do/retourFormulaire], o navegador reenvia ao servidor os dois cookies que recebeu.

  • linhas 4-18: recuperam-se os valores dos cookies denominados “nome” e “idade”. Curiosamente, não existe um método que permita obter o valor de um cookie a partir de sua chave. Por isso, é necessário examinar cada um dos cookies recebidos.
  • Feito isso, os dois valores obtidos são inseridos no modelo da visualização [formulaire] (linhas 20-21) para que ela os exiba.

10.6. Tests

Inicie ou reinicie o Tomcat após integrar o projeto Eclipse [personne-mvc-05] e, em seguida, acesse a URL [http://localhost:8080/personne5].