Skip to content

10. Applicazione web MVC [personne] – versione 5

10.1. Introduction

In questa versione apportiamo due modifiche:

La prima riguarda il modo in cui il client indica al server l’azione che desidera eseguire. Finora questa veniva specificata tramite un parametro denominato [action] nella richiesta del client GET o POST. In questo caso, l’azione sarà specificata dall’ultimo elemento dell’URL richiesto dal client, come mostra la seguente sequenza:

Image

In [1], l’URL a cui è stato inviato il modulo è [/personne5/do/validationFormulaire]. È l’ultimo elemento [validationFormulaire] dell’URL che ha permesso al controller di riconoscere l’azione da eseguire. In [2], l'azione POST, generata dal link [Retour au formulaire], è stata eseguita all'URL [/personne5/do/retourFormulaire]. Anche in questo caso, l’ultimo elemento [retourFormulaire] dell’URL indica al controller l’azione da eseguire.

Introduciamo questa modifica perché è il metodo utilizzato dai framework di sviluppo web più diffusi, come Struts o Spring MVC.

Tutti gli URL dell'applicazione avranno il formato [/personne5/do/action]. Il file [web.xml] dell'applicazione [/personne5] indicherà che quest'ultima accetta URL del formato [/do/*]:


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

Il controller recupererà il nome dell'azione da eseguire nel modo seguente:

         // si recupera l'azione da eseguire
String action=request.getPathInfo();

Il metodo [getPathInfo] dell’oggetto [request] restituisce l’ultimo elemento dell’URL della richiesta.

La seconda modifica riguarda il modo in cui vengono memorizzati i dati inseriti dall'utente tra due cicli di richiesta/risposta. Al momento, queste informazioni vengono memorizzate in una sessione. Questo metodo può presentare degli svantaggi se ci sono molti utenti e molti dati da memorizzare per ciascuno di essi. Infatti, ogni utente ha la propria sessione personale. Inoltre, la sessione rimane attiva per un certo tempo dopo che l’utente si è disconnesso, a meno che non sia stata prevista un’opzione di disconnessione. Pertanto, 1000 sessioni da 1000 byte occuperanno 1 MB di memoria. Si tratta comunque di un requisito moderato e sono poche le applicazioni che hanno 1000 sessioni attive contemporaneamente.

Tuttavia, esistono alternative alla sessione, meno onerose in termini di memoria, ed è bene conoscerle. In questo caso utilizzeremo il metodo dei cookie. Illustriamolo con un esempio.


Fase 1: l’utente invia un modulo:


Questo ciclo richiesta/risposta dà luogo ai seguenti scambi HTTP tra il client e il server:

[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/modulo
Cookie: JSESSIONID=6C6F4D112803A7E3696D41F5750CEDE7
Content-Type: application/x-www-form-urlencoded
Content-Length: 24

txtNom=pauline&txtAge=18

Si tratta di un classico POST. Non c'è nulla di particolare da segnalare qui, se non che, sebbene non si intenda utilizzare una sessione, il server web ne crea comunque una. Lo si nota dal token di sessione che il browser rinvia al server alla riga 11 e che aveva ricevuto in precedenza dal server.

[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

Si nota che alle righe 3 e 4 sono state inviate al browser client le intestazioni HTTP e [Set-Cookie], una per il nome (riga 3) e una per l’età (riga 4). I valori di questi cookie corrispondono a quelli inviati alla riga 14 dei cookie POST e [1] sopra riportati.


Fase 2: Ritorno al modulo


Image

Questo ciclo richiesta/risposta dà luogo ai seguenti scambi HTTP tra il client e il server:

[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

Qui assistiamo al POST generato dal clic sul link [Retour au formulaire]. Alla riga 11, vediamo che il browser rinvia al server i cookie che ha ricevuto ([nom, age, JSESSIONID]) tramite l’intestazione HTTP [Cookie]. Questo è il principio di funzionamento dei cookie: il client rinvia al server i cookie che quest’ultimo gli ha inviato. In questo esempio, il controller riceverà i valori [pauline, 18] che dovrà inserire nei campi [txtNom, txtAge] della vista [formulaire] visualizzata in [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

Non c'è nulla di particolare da segnalare, se non il fatto che in questa risposta il server non ha inviato alcun cookie. Ciò non impedirà al browser di rinviare, nel prossimo scambio, tutti i cookie ricevuti dal server, anche se ciò non serve a nulla. Si riduce quindi la pressione sulla memoria disponibile del server a costo di un aumento del flusso di caratteri negli scambi client/server.

10.2. Il progetto Eclipse

Per creare il progetto Eclipse [mvc-personne-05] dell’applicazione web [/personne5], si duplicherà il progetto [mvc-personne-04] seguendo la procedura descritta al paragrafo 6.2.

10.3. Configurazione dell'applicazione web [personne5]

Il file web.xml dell’applicazione /personne5 è il seguente:


<?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>
    <!--  Mappatura ServletPersonne-->
    <servlet-mapping>
        <servlet-name>personne</servlet-name>
        <url-pattern>/do/*</url-pattern>
    </servlet-mapping>
    <!--  file di benvenuto -->
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

Questo file è identico a quello della versione precedente, tranne che per alcuni dettagli:

  • riga 6: il nome visualizzato dell’applicazione web è cambiato in [mvc-personne-05]
  • riga 18: gli URL gestiti dall’applicazione hanno il formato [/do/*]. In precedenza, veniva gestito solo l’URL [/main]. Ora, il numero di URL corrisponde a quello delle azioni da elaborare.

La pagina iniziale [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"/>
  • riga 5: la pagina [index.jsp] reindirizza il client all'URL [/personne5/do/formulaire], il che equivale a richiedere al controller di eseguire l'azione [formulaire].

10.4. Il codice delle viste

Le viste [formulaire, réponse, erreurs] subiscono poche modifiche. L'unica modifica deriva dal fatto che l'azione da eseguire non è più specificata come in precedenza, quando era definita in un campo nascosto denominato [action] dei moduli inviati via POST. Ora è definita nell’URL di destinazione dei moduli inviati, c.a.d, nell’attributo [action] del 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>
  • riga [13]: il parametro [action] del modulo ricompare dopo essere scomparso per un certo periodo nelle versioni precedenti. Per comprendere il valore di questo attributo in questo contesto, occorre ricordare che tutti gli URL elaborati dall’applicazione hanno la forma [/do/action]. Riga [13]: l’attributo [action] ha come valore un URL relativo (che non inizia con /). Pertanto, il browser lo completerà con l’URL della pagina attualmente visualizzata, quindi necessariamente un URL della forma [/do/action]. L’ultimo elemento verrà sostituito dall’URL relativo dell’attributo [action] del tag <form> per ottenere l’URL [/do/validationFormulaire] come destinazione di POST.
  • Il campo nascosto [action] è scomparso

[réponse.jsp]:


...

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

  • riga [7]: la destinazione di POST sarà [/do/retourFormulaire]
  • il campo nascosto [action] è scomparso nel modulo delle righe 7-8.

[erreurs.jsp]:


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

  • riga [6]: la destinazione di POST sarà [/do/retourFormulaire]
  • il campo nascosto [action] è scomparso nel modulo alle righe 6-7.

Si invita il lettore a testare queste nuove viste seguendo il principio illustrato nelle versioni precedenti.

10.5. Il controller [ServletPersonne]

Il controller [ServletPersonne] dell’applicazione web [/personne5] gestirà le seguenti azioni:

n.
richiesta
origine
elaborazione
1
[GET /personne5/do/formulaire]
URL digitato dall'utente
- inviare la vista [formulaire] vuota
2
[POST
/persona5/do/validationFormulaire]
con i parametri [txtNom, txtAge]
pubblicati
cliccando sul pulsante
[Envoyer] nella vista
[formulaire]
- verificare i valori dei parametri [txtNom, txtAge]
- se non sono corretti, inviare la vista [erreurs(erreurs)]
- se sono corretti, inviare la vista [reponse(nom,age)]
3
[POST
/persona5/do/retourFormulaire]
senza parametri inviati
clic sul link [Torna al
modulo] delle visualizzazioni
[réponse] e [erreurs].
- inviare la vista [formulaire] precompilata con gli ultimi valori inseriti

Lo scheletro del controller [ServletPersonne] è identico a quello della versione precedente. Esaminiamo le modifiche apportate ai metodi [doValidationFormulaire, doRetourFormulaire, doGet], poiché i metodi [init, doInit, doPost] non subiscono variazioni.

10.5.1. Il metodo [doGet]

Il metodo [doGet] non recupera l’azione da eseguire allo stesso modo delle versioni precedenti:

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

         // si verifica come è avvenuta l'inizializzazione del servlet
        if (erreursInitialisation.size() != 0) {
...
        }
         // si recupera il metodo di invio della richiesta
        String méthode=request.getMethod().toLowerCase();
         // si recupera l'azione da eseguire
        String action=request.getPathInfo();
         // azione?
        if(action==null){
            action="/formulaire";
        }
         // esecuzione dell'azione
        if(méthode.equals("get") && action.equals("/formulaire")){
             // avvio dell'applicazione
            doInit(request,response);
            return;
        }
        if(méthode.equals("post") && action.equals("/validationFormulaire")){
             // convalida del modulo di inserimento
            doValidationFormulaire(request,response);
            return;
        }
        if(méthode.equals("post") && action.equals("/retourFormulaire")){
             // ritorno al modulo di inserimento
            doRetourFormulaire(request,response);
            return;
        }
         // altri casi
        doInit(request,response);
    }
  • riga 12: si recupera l'azione da eseguire. Essa ha la forma [/action].
  • righe 18-22: elaborazione dell’azione [/formulaire] richiesta da una richiesta GET
  • righe 23-27: elaborazione dell'azione [/validationFormulaire] richiesta da una richiesta POST
  • righe 28-32: elaborazione dell'azione [/retourFormulaire] richiesta da una richiesta POST

10.5.2. Il metodo [doValidationFormulaire]

Questo metodo elabora la richiesta n. 2 [POST /personne5/do/validationFormulaire] con [txtNom, txtAge] negli elementi inviati. Il suo codice è il seguente:

// convalida del modulo
    void doValidationFormulaire(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException{
         // si recuperano i parametri
        String nom = request.getParameter("txtNom");
        String age = request.getParameter("txtAge");
         // che vengono memorizzati in un cookie
        response.addCookie(new Cookie("nom",nom));
        response.addCookie(new Cookie("age",age));
         // verifica dei parametri
        ...
    }

Novità:

  • il metodo [doValidationFormulaire] invia in risposta una delle viste [réponse, erreurs]. Indipendentemente dalla risposta, il controller inserisce due cookie, righe 8-9. Un cookie è rappresentato da un oggetto [Cookie] il cui costruttore accetta due parametri: la chiave del cookie e il valore ad essa associato.
  • riga 8: il valore inserito per il nome viene inserito in un cookie con chiave "nom"
  • riga 9: il valore inserito per l’età viene inserito in un cookie con chiave "age"
  • Un cookie viene aggiunto alla risposta HTTP inviata al client tramite il metodo [response.addCookie]. Questa risposta viene qui solo preparata. Verrà effettivamente inviata solo quando verrà eseguita la pagina JSP della vista inviata al client.

10.5.3. Il metodo [doRetourFormulaire]

Questo metodo elabora la richiesta n. 2 [POST /personne5/do/retourFormulaire] senza elementi inviati. Il suo codice è il seguente:

         // visualizzazione del modulo precompilato
    void doRetourFormulaire(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
         // si recuperano i cookie dell'utente
        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++;
                }
            }
        }
         // si prepara il modello del modulo
        request.setAttribute("nom",nom);
        request.setAttribute("age",age);
         // viene visualizzato il modulo
        getServletContext().getRequestDispatcher((String)params.get("urlFormulaire")).forward(
                request, response);
        return;
    }

Novità:

Il metodo [doRetourFormulaire] deve visualizzare un modulo precompilato con gli ultimi dati inseriti. Nella versione precedente, questi dati erano memorizzati nella sessione. In questa versione, non si utilizza più la sessione, ma i cookie per memorizzare gli elementi tra uno scambio e l’altro tra client e server. Quando il cliente ha richiesto la convalida del modulo, ha ricevuto in risposta la vista [réponse] o [erreurs] a seconda dei casi, accompagnata da due cookie denominati "nome" e "età". Quando si clicca sul link [Retour au formulaire] presente in queste due viste, che genera un POST sull’URL [/do/retourFormulaire], il browser rinvierà al server i due cookie che ha ricevuto.

  • righe 4-18: si recuperano i valori dei cookie denominati "nome" e "età". Stranamente, non esiste un metodo che consenta di ottenere il valore di un cookie a partire dalla sua chiave. Pertanto, si è costretti a esaminare uno per uno tutti i cookie ricevuti.
  • Fatto ciò, i due valori ottenuti vengono inseriti nel modello della vista [formulaire] (righe 20-21) affinché questa li visualizzi.

10.6. Tests

Avviare o riavviare Tomcat dopo avervi integrato il progetto Eclipse [personne-mvc-05], quindi richiedere l’URL [http://localhost:8080/personne5].