Skip to content

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

10.1. Introduction

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

A primeira diz respeito à forma como o cliente indica ao servidor a ação que pretende realizar. Até agora, esta era especificada através de um parâmetro denominado [action] na solicitação do GET ou do POST do cliente. Neste caso, a ação será especificada pelo último elemento do URL solicitado pelo cliente, tal como mostra a sequência seguinte:

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 realizar. Em [2], o POST, provocado pelo link [Retour au formulaire], foi realizado na URL [/personne5/do/retourFormulaire]. Mais uma vez, o último elemento [retourFormulaire] da URL indica ao controlador a ação a realizar.

Estamos a introduzir esta alteração porque este é o método utilizado pelos frameworks de desenvolvimento web mais comuns, tais como o Struts ou o Spring MVC.

Todas as URLs da aplicação terão o formato [/personne5/do/action]. O ficheiro [web.xml] da aplicação [/personne5] indicará que esta aceita URLs com o formato [/do/*]:


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

O controlador irá recuperar o nome da ação a realizar da seguinte forma:

         // recuperamos a ação a executar
String action=request.getPathInfo();

O método [getPathInfo] do objeto [request] fornece o último elemento da URL do pedido.

A segunda alteração introduzida diz respeito à forma de armazenar as entradas feitas pelo utilizador entre dois ciclos de pedido/resposta. Atualmente, estas informações são armazenadas numa sessão. Este método pode apresentar desvantagens se houver muitos utilizadores e muitos dados a armazenar para cada um deles. Com efeito, cada utilizador tem a sua sessão pessoal. Além disso, esta permanece ativa durante algum tempo após a saída de um utilizador, a menos que se tenha tido o cuidado de lhe oferecer uma opção de desligar a sessão. Assim, 1000 sessões de 1000 octetos ocuparão 1 Mo de memória. Trata-se de uma exigência moderada e há poucas aplicações que tenham 1000 sessões ativas simultaneamente.

No entanto, existem alternativas à sessão, menos exigentes em termos de memória, e é bom conhecê-las. Aqui, utilizaremos o método dos cookies. Vamos ilustrá-lo com um exemplo.


Passo 1: o utilizador submete um formulário:


Este ciclo de pedido/resposta dá origem às 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 assinalar aqui, a não ser que, embora não se vá utilizar uma sessão, o servidor web cria uma na mesma. Isso é visível no token de sessão que o navegador reenvia ao servidor na linha 11 e que tinha 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, foram enviados ao navegador do cliente os cabeçalhos HTTP e [Set-Cookie], um para o nome (linha 3) e outro para a idade (linha 4). Os valores destes cookies são os valores enviados na linha 14 dos POST e [1] acima referidos.


Passo 2: Regresso ao formulário


Image

Este ciclo de pedido/resposta dá origem às seguintes trocas 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

Assistimos aqui ao POST provocado pelo clique no link [Retour au formulaire]. Na linha 11, vemos que o navegador reenvia ao servidor os cookies que recebeu ([nom, age, JSESSIONID]) através do cabeçalho HTTP [Cookie]. Este é o princípio dos cookies. O cliente reenvia ao servidor os cookies que este lhe enviou. Neste exemplo, o controlador irá receber os valores [pauline, 18], que deve colocar nos campos [txtNom, txtAge] da vista [formulaire] apresentada 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á aqui nada de especial a assinalar, para além do facto de, nesta resposta, o servidor não ter enviado cookies. Isso não impedirá o navegador de reenviar, na próxima troca de dados, todos os cookies que recebeu do servidor, mesmo que isso não sirva para nada. Reduz-se, assim, a pressão sobre a memória disponível do servidor, à custa de um aumento do 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 ficheiro 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>
    <!--  ficheiros de início -->
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

Este ficheiro é idêntico ao da versão anterior, com exceção de alguns detalhes:

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

A página inicial [index.jsp] altera-se:


<%@ 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 vistas

As vistas [formulaire, réponse, erreurs] sofrem poucas alterações. A única alteração deve-se ao facto de a ação a realizar já não ser especificada da mesma forma que anteriormente, quando era definida num campo oculto denominado [action] dos formulários enviados. Agora, é definida no 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 durante algum tempo nas versões anteriores. Para compreender o valor deste atributo aqui, é necessário lembrar que todas as URLs processadas pela aplicação têm o formato [/do/action]. Na linha [13], o atributo [action] tem como valor uma URL relativa (que não começa por /). Assim, o navegador irá completá-la com a URL da página atualmente exibida, ou seja, necessariamente uma URL com o formato [/do/action]. O último elemento será substituído pela URL relativa do atributo [action] da baliza <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á o [/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.

Convida-se o leitor a testar estas novas vistas de acordo com o princípio apresentado nas versões anteriores.

10.5. O controlador [ServletPersonne]

O controlador [ServletPersonne] da aplicação web [/personne5] irá processar as seguintes ações:

n.º
pedido
origem
processamento
1
[GET /personne5/do/formulaire]
URL introduzida pelo utilizador
- enviar a vista [formulaire] vazia
2
[POST
/pessoa5/do/validationFormulaire]
com os parâmetros [txtNom, txtAge]
publicados
clicando no botão
[Envoyer] na vista
[formulaire]
- verificar os valores dos parâmetros [txtNom, txtAge]
- se estiverem incorretos, enviar a vista [erreurs(erreurs)]
- se estiverem corretos, enviar a vista [reponse(nom,age)]
3
[POST
/pessoa5/para/retourFormulaire]
sem parâmetros enviados
clique na ligação [Voltar ao
formulário] das visualizações
[réponse] e [erreurs].
- enviar a vista [formulaire] pré-preenchida com os últimos valores introduzidos

A estrutura do controlador [ServletPersonne] é idêntica à da versão anterior. Analisamos as alterações introduzidas nos métodos [doValidationFormulaire, doRetourFormulaire, doGet], uma vez 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 executar da mesma forma que nas versões anteriores:

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

         // verifica-se como decorreu 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 executar
        String action=request.getPathInfo();
         // ação?
        if(action==null){
            action="/formulaire";
        }
         // execução da ação
        if(méthode.equals("get") && action.equals("/formulaire")){
             // início da aplicação
            doInit(request,response);
            return;
        }
        if(méthode.equals("post") && action.equals("/validationFormulaire")){
             // validação do formulário de introdução de dados
            doValidationFormulaire(request,response);
            return;
        }
        if(méthode.equals("post") && action.equals("/retourFormulaire")){
             // regresso ao formulário de introdução de dados
            doRetourFormulaire(request,response);
            return;
        }
         // outros casos
        doInit(request,response);
    }
  • linha 12: recupera-se a ação a executar. 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 requisiçã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 enviados. O seu código é o seguinte:

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

Novidades:

  • o método [doValidationFormulaire] envia como resposta uma das vistas [réponse, erreurs]. Independentemente dessa 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 introduzido para o nome é colocado num cookie com a chave «nom»
  • linha 9: o valor introduzido para a idade é colocado num cookie com a chave «idade»
  • É adicionado um cookie à resposta HTTP enviada ao cliente através do método [response.addCookie]. Esta resposta é, neste caso, apenas preparada. Só será efetivamente enviada quando a página JSP da vista enviada ao cliente for executada.

10.5.3. O método [doRetourFormulaire]

Este método processa o pedido n.º 2 [POST /personne5/do/retourFormulaire] sem elementos enviados. O 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 utilizador
        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);
         // é apresentado o formulário
        getServletContext().getRequestDispatcher((String)params.get("urlFormulaire")).forward(
                request, response);
        return;
    }

Novidades:

O método [doRetourFormulaire] deve apresentar um formulário pré-preenchido com as últimas entradas efetuadas. Na versão anterior, estas estavam na sessão. Nesta versão, já não se utiliza 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 vista [réponse] ou [erreurs], consoante o caso, acompanhada de dois cookies denominados «nome» e «idade». Ao clicar na ligação [Retour au formulaire] destas duas vistas, o que provoca a exibição de 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 da sua chave. Por isso, é necessário analisar cada um dos cookies recebidos.
  • Feito isto, os dois valores obtidos são colocados no modelo da vista [formulaire] (linhas 20-21) para que esta os exiba.

10.6. Tests

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