Skip to content

10. Aplicación web MVC [personne] – versión 5

10.1. Introducción

En esta versión, introducimos dos modificaciones:

La primera se refiere a la forma en que el cliente indica al servidor la acción que desea realizar. Hasta ahora, esta se especificaba mediante un parámetro denominado [action] en la solicitud del GET o del POST del cliente. En este caso, la acción se especificará mediante el último elemento de la URL solicitada por el cliente, tal y como se muestra en la siguiente secuencia:

Image

En [1], la URL a la que se ha enviado el formulario es [/personne5/do/validationFormulaire]. Es el último elemento, [validationFormulaire], de la URL el que ha permitido al controlador reconocer la acción que debe realizar. En [2], la acción POST, provocada por el enlace [Retour au formulaire], se realizó en la URL [/personne5/do/retourFormulaire]. Una vez más, el último elemento [retourFormulaire] de la URL indica al controlador la acción que debe realizar.

Introducimos este cambio porque es el método utilizado por los marcos de desarrollo web más extendidos, como Struts o Spring MVC.

Todas las URL de la aplicación tendrán el formato [/personne5/do/action]. El archivo [web.xml] de la aplicación [/personne5] indicará que esta acepta URL con el formato [/do/*]:


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

El controlador recuperará el nombre de la acción que se debe realizar de la siguiente manera:

        // se recupera la acción que se debe ejecutar
String action=request.getPathInfo();

El método [getPathInfo] del objeto [request] proporciona el último elemento de la URL de la solicitud.

La segunda modificación se refiere a la forma de almacenar los datos introducidos por el usuario entre dos ciclos de solicitud/respuesta. Por el momento, esta información se almacena en una sesión. Este método puede presentar inconvenientes si hay muchos usuarios y una gran cantidad de datos que almacenar para cada uno de ellos. De hecho, cada usuario tiene su propia sesión. Además, esta permanece activa durante un tiempo después de que el usuario se haya desconectado, a menos que se le haya ofrecido una opción para hacerlo. Así, 1000 sesiones de 1000 bytes ocuparán 1 MB de memoria. Se trata de un requisito moderado y son pocas las aplicaciones que tienen 1000 sesiones activas simultáneamente.

No obstante, existen alternativas a la sesión que consumen menos memoria y conviene conocerlas. En este caso utilizaremos el método de las cookies. Veamos un ejemplo.


Paso 1: el usuario envía un formulario:


Este ciclo de solicitud/respuesta da lugar a los siguientes intercambios HTTP entre el cliente y el 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/persona5/do/formulario
Cookie: JSESSIONID=6C6F4D112803A7E3696D41F5750CEDE7
Content-Type: application/x-www-form-urlencoded
Content-Length: 24

txtNom=pauline&txtAge=18

Se trata de un POST clásico. No hay nada especial que destacar aquí, salvo que, aunque no se vaya a utilizar una sesión, el servidor web crea una de todos modos. Esto se aprecia en el token de sesión que el navegador devuelve al servidor en la línea 11 y que había recibido previamente del 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

Se observa que, en las líneas 3 y 4, se han enviado al navegador del cliente los encabezados HTTP y [Set-Cookie], uno para el nombre (línea 3) y otro para la edad (línea 4). Los valores de estas cookies son los valores enviados en la línea 14 de POST y [1], mencionados anteriormente.


Paso 2: Volver al formulario


Image

Este ciclo de solicitud/respuesta da lugar a los siguientes intercambios HTTP entre el cliente y el 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/persona5/do/validationFormulaire
Cookie: nom=pauline; age=18; JSESSIONID=6C6F4D112803A7E3696D41F5750CEDE7
Content-Type: application/x-www-form-urlencoded
Content-Length: 0

Aquí vemos el POST provocado al hacer clic en el enlace [Retour au formulaire]. En la línea 11, vemos que el navegador devuelve al servidor las cookies que ha recibido ([nom, age, JSESSIONID]) mediante el encabezado HTTP [Cookie]. Este es el principio en el que se basan las cookies: el cliente devuelve al servidor las cookies que este le ha enviado. En este ejemplo, el controlador recibirá los valores [pauline, 18], que deberá colocar en los campos [txtNom, txtAge] de la vista [formulaire] mostrada en [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

No hay nada en particular que destacar, salvo el hecho de que, en esta respuesta, el servidor no ha enviado cookies. Esto no impedirá que el navegador reenvíe en el siguiente intercambio todas las cookies que haya recibido del servidor, aunque no sirva de nada. De este modo, se reduce la carga sobre la memoria disponible del servidor a costa de un aumento del flujo de caracteres en los intercambios entre el cliente y el servidor.

10.2. El proyecto Eclipse

Para crear el proyecto Eclipse [mvc-personne-05] de la aplicación web [/personne5], se duplicará el proyecto [mvc-personne-04] siguiendo el procedimiento descrito en el apartado 6.2.

10.3. Configuración de la aplicación web [personne5]

El archivo web.xml de la aplicación /personne5 es el siguiente:


<?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>
    <!--  Asignación ServletPersonne-->
    <servlet-mapping>
        <servlet-name>personne</servlet-name>
        <url-pattern>/do/*</url-pattern>
    </servlet-mapping>
    <!--  archivos de inicio -->
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

Este archivo es idéntico al de la versión anterior, salvo por algunos detalles:

  • línea 6: el nombre de visualización de la aplicación web ha cambiado a [mvc-personne-05]
  • línea 18: las URL procesadas por la aplicación tienen el formato [/do/*]. Anteriormente, solo se procesaba la URL [/main]. Ahora hay tantas URL como acciones que procesar.

La página de inicio [index.jsp] cambia:


<%@ 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"/>
  • línea 5: la página [index.jsp] redirige al cliente a la URL [/personne5/do/formulaire], lo que equivale a pedir al controlador que ejecute la acción [formulaire].

10.4. El código de las vistas

Las vistas [formulaire, réponse, erreurs] apenas cambian. El único cambio radica en que la acción que se debe realizar ya no se especifica de la misma forma que antes, cuando se definía en un campo oculto denominado [action] de los formularios enviados. Ahora se define en la URL de destino de los formularios enviados, c.a.d, en el atributo [action] de la etiqueta <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>
  • Línea [13]: el parámetro [action] del formulario vuelve a aparecer tras haber desaparecido durante un tiempo en las versiones anteriores. Para comprender el valor de este atributo aquí, hay que recordar que todas las URL procesadas por la aplicación tienen el formato [/do/action]. En la línea [13], el atributo [action] tiene como valor una URL relativa (que no comienza por /). Por lo tanto, el navegador la completará con la URL de la página que se está mostrando actualmente, es decir, necesariamente una URL con el formato [/do/action]. El último elemento se sustituirá por la URL relativa del atributo [action] de la etiqueta <form> para obtener la URL [/do/validationFormulaire] como destino de POST.
  • El campo oculto [action] ha desaparecido

[réponse.jsp]:


...

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

  • línea [7]: el destino de POST será [/do/retourFormulaire]
  • El campo oculto [action] ha desaparecido en el formulario de las líneas 7-8.

[erreurs.jsp]:


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

  • línea [6]: el destino de POST será [/do/retourFormulaire]
  • El campo oculto [action] ha desaparecido del formulario de las líneas 6-7.

Se invita al lector a probar estas nuevas vistas siguiendo el principio visto en las versiones anteriores.

10.5. El controlador [ServletPersonne]

El controlador [ServletPersonne] de la aplicación web [/personne5] gestionará las siguientes acciones:

n.º
solicitud
origen
tratamiento
1
[GET /personne5/do/formulaire]
URL introducida por el usuario
- enviar la vista [formulaire] vacía
2
[POST
/persona5/do/validationFormulaire]
con los parámetros [txtNom, txtAge]
publicados
al hacer clic en el botón
[Envoyer] de la vista
[formulaire]
- comprueba los valores de los parámetros [txtNom, txtAge]
- si son incorrectos, enviar la vista [erreurs(erreurs)]
- si son correctos, enviar la vista [reponse(nom,age)]
3
[POST
/persona5/do/retourFormulaire]
sin parámetros enviados
haz clic en el enlace [Volver al
formulario] de las vistas
[réponse] y [erreurs].
- Enviar la vista [formulaire] prellenada con los últimos valores introducidos

La estructura del controlador [ServletPersonne] es idéntica a la de la versión anterior. Repasamos los cambios introducidos en los métodos [doValidationFormulaire, doRetourFormulaire, doGet], ya que los métodos [init, doInit, doPost] no han variado.

10.5.1. El método [doGet]

El método [doGet] no recupera la acción que se debe ejecutar de la misma forma que en las versiones anteriores:

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

        // se comprueba cómo se ha realizado la inicialización del servlet
        if (erreursInitialisation.size() != 0) {
...
        }
        // se recupera el método de envío de la solicitud
        String méthode=request.getMethod().toLowerCase();
        // se recupera la acción que se va a ejecutar
        String action=request.getPathInfo();
        // ¿Acción?
        if(action==null){
            action="/formulaire";
        }
        // ejecución de la acción
        if(méthode.equals("get") && action.equals("/formulaire")){
            // inicio de la aplicación
            doInit(request,response);
            return;
        }
        if(méthode.equals("post") && action.equals("/validationFormulaire")){
            // validación del formulario de introducción de datos
            doValidationFormulaire(request,response);
            return;
        }
        if(méthode.equals("post") && action.equals("/retourFormulaire")){
            // vuelta al formulario de introducción de datos
            doRetourFormulaire(request,response);
            return;
        }
        // otros casos
        doInit(request,response);
    }
  • línea 12: se recupera la acción que se debe ejecutar. Tiene el formato [/action].
  • líneas 18-22: procesamiento de la acción [/formulaire] solicitada por una consulta GET
  • líneas 23-27: procesamiento de la acción [/validationFormulaire] solicitada por una solicitud POST
  • líneas 28-32: procesamiento de la acción [/retourFormulaire] solicitada por una consulta POST

10.5.2. El método [doValidationFormulaire]

Este método procesa la solicitud n.º 2 [POST /personne5/do/validationFormulaire] junto con [txtNom, txtAge] en los elementos enviados. Su código es el siguiente:

// validación del formulario
    void doValidationFormulaire(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException{
        // se recuperan los parámetros
        String nom = request.getParameter("txtNom");
        String age = request.getParameter("txtAge");
        // que se almacenan en una cookie
        response.addCookie(new Cookie("nom",nom));
        response.addCookie(new Cookie("age",age));
        // verificación de los parámetros
        ...
    }

Novedades:

  • el método [doValidationFormulaire] envía como respuesta una de las vistas [réponse, erreurs]. Sea cual sea esta respuesta, el controlador incluye en ella dos cookies, líneas 8-9. Una cookie está representada por un objeto [Cookie] cuyo constructor admite dos parámetros: la clave de la cookie y el valor asociado a ella.
  • línea 8: el valor introducido para el nombre se guarda en una cookie con la clave «nombre»
  • línea 9: el valor introducido para la edad se guarda en una cookie con la clave «age»
  • Se añade una cookie a la respuesta HTTP enviada al cliente mediante el método [response.addCookie]. Esta respuesta solo se prepara en este momento. Solo se enviará realmente cuando se ejecute la página JSP de la vista enviada al cliente.

10.5.3. El método [doRetourFormulaire]

Este método procesa la solicitud n.º 2 [POST /personne5/do/retourFormulaire] sin elementos enviados. Su código es el siguiente:

        // Visualización del formulario prellenado
    void doRetourFormulaire(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        // se recuperan las cookies del usuario
        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++;
                }
            }
        }
        // se prepara la plantilla del formulario
        request.setAttribute("nom",nom);
        request.setAttribute("age",age);
        // se muestra el formulario
        getServletContext().getRequestDispatcher((String)params.get("urlFormulaire")).forward(
                request, response);
        return;
    }

Novedades:

El método [doRetourFormulaire] debe mostrar un formulario prellenado con las últimas entradas realizadas. En la versión anterior, estas se almacenaban en la sesión. En esta versión, ya no se utiliza la sesión, sino cookies para memorizar los datos entre dos intercambios cliente-servidor. Cuando el cliente solicitó la validación del formulario, recibió como respuesta la vista [réponse] o [erreurs], según el caso, acompañada de dos cookies denominadas «nombre» y «edad». Al hacer clic en el enlace [Retour au formulaire] de estas dos vistas, lo que provoca la aparición de POST en la URL [/do/retourFormulaire], el navegador reenviará al servidor las dos cookies que ha recibido.

  • Líneas 4-18: se recuperan los valores de las cookies denominadas «nombre» y «edad». Curiosamente, no existe ningún método que permita obtener el valor de una cookie a partir de su clave. Por lo tanto, nos vemos obligados a revisar cada una de las cookies recibidas.
  • Una vez hecho esto, los dos valores obtenidos se colocan en la plantilla de la vista [formulaire] (líneas 20-21) para que esta los muestre.

10.6. Pruebas

Inicia o reinicia Tomcat tras haber integrado en él el proyecto de Eclipse [personne-mvc-05] y, a continuación, accede a la URL [http://localhost:8080/personne5].