Skip to content

10. Application web MVC [personne] – version 5

10.1. Introduction

Dans cette version, nous apportons deux modifications :

La première porte sur la façon utilisée par le client pour indiquer au serveur l'action qu'il désire faire. Jusqu'à maintenant celle-ci était précisée à l'aide d'un paramètre appelé [action] dans la requête du GET ou du POST du client. Ici, l'action sera précisée par le dernier élément de l'Url demandée par le client comme le montre la séquence suivante :

Image

En [1], l'Url à laquelle a été postée le formulaire est [/personne5/do/validationFormulaire]. C'est le dernier élément [validationFormulaire] de l'Url qui a permis au contrôleur de reconnaître l'action à faire. En [2], le POST provoqué par le lien [Retour au formulaire] a été fait à l'Url [/personne5/do/retourFormulaire]. Là encore, le dernier élément [retourFormulaire] de l'Url indique au contrôleur l'action à faire.

Nous introduisons cette modification parce que c'est la méthode utilisée par les frameworks de développement web les plus répandus tels Struts ou Spring MVC.

Toutes les Url de l'application auront la forme [/personne5/do/action]. Le fichier [web.xml] de l'application [/personne5] indiquera que celle-ci accepte des Url de la forme [/do/*] :


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

Le contrôleur récupérera le nom de l'action à faire de la façon suivante :

        // on récupère l'action à exécuter
String action=request.getPathInfo();

La méthode [getPathInfo] de l'objet [request] donne le dernier élément de l'Url de la requête.

La seconde modification apportée porte sur la façon de mémoriser les saisies faites par l'utilisateur entre deux cycles demande / réponse. Pour l'instant, ces informations sont mémorisées dans une session. Cette méthode peut présenter des inconvénients s'il y a beaucoup d'utilisateurs et beaucoup de données à mémoriser pour chacun d'eux. En effet, chaque utilisateur a sa session personnelle. De plus celle-ci reste active un certain temps après le départ d'un utilisateur sauf si on a pris soin de lui offrir une option de déconnexion. Ainsi 1000 sessions de 1000 octets vont occuper 1Mo de mémoire. Cela reste une exigence modérée et il y a peu d'applications qui ont 1000 sessions actives simultanément.

Néanmoins, il existe des alternatives à la session, moins coûteuses en mémoire et il est bon de les connaître. Nous utiliserons ici la méthode des cookies. Illustrons-là sur un exemple.


Etape 1 : l'utilisateur valide un formulaire :


Ce cycle demande / réponse donne lieu aux échanges HTTP suivants entre le client et le serveur :

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

txtNom=pauline&txtAge=18

C'est un POST classique. Il n'y a rien de particulier à signaler ici, si ce n'est que bien qu'on ne va pas utiliser de session, le serveur web en crée une quand même. On le voit au jeton de session que le navigateur renvoie au serveur ligne 11 et qu'il avait reçu précédemment du serveur.

[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

On voit que lignes 3 et 4, des entêtes HTTP [Set-Cookie] ont été envoyés au navigateur client, un pour le nom (ligne 3) et un pour l'âge (ligne 4). Les valeurs de ces cookies sont les valeurs postées ligne 14 du POST [1] ci-dessus.


Etape 2 : Retour au formulaire


Image

Ce cycle demande / réponse donne lieu aux échanges HTTP suivants entre le client et le serveur :

[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/personne5/do/validationFormulaire
Cookie: nom=pauline; age=18; JSESSIONID=6C6F4D112803A7E3696D41F5750CEDE7
Content-Type: application/x-www-form-urlencoded
Content-Length: 0

Nous assistons ici au POST provoqué par le clic sur le lien [Retour au formulaire]. Ligne 11, nous voyons que le navigateur renvoie au serveur, les cookies qu'il a reçus [nom, age, JSESSIONID] au moyen de l'entête Http [Cookie]. C'est le principe des cookies. Le client renvoie au navigateur les cookies que celui-ci lui a envoyés. Dans cet exemple, le contrôleur va recevoir les valeurs [pauline, 18] qu'il doit placer dans les champs [txtNom, txtAge] de la vue [formulaire] affichée 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

Il n'y a là rien à signaler de particulier autre que le fait que dans cette réponse, le serveur n'a pas envoyé de cookies. Cela n'empêchera pas le navigateur de renvoyer au prochain échange tous les cookies qu'il a reçus du serveur même si cela ne sert à rien. On diminue donc la pression sur la mémoire disponible du serveur au prix d'une augmentation du flux de caractères dans les échanges client / serveur.

10.2. Le projet Eclipse

Pour créer le projet Eclipse [mvc-personne-05] de l'application web [/personne5], on dupliquera le projet [mvc-personne-04] en suivant la procédure décrite au paragraphe 6.2.

10.3. Configuration de l'application web [personne5]

Le fichier web.xml de l'application /personne5 est le suivant :


<?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>
    <!--  Mapping ServletPersonne-->
    <servlet-mapping>
        <servlet-name>personne</servlet-name>
        <url-pattern>/do/*</url-pattern>
    </servlet-mapping>
    <!--  fichiers d'accueil -->
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

Ce fichier est identique à celui de la version précédente hormis quelques détails :

  • ligne 6 : le nom d'affichage de l'application web a changé en [mvc-personne-05]
  • ligne 18 : les Url traitées par l'application sont de la forme [/do/*]. Auparavant, seule l'Url [/main] était traitée. Maintenant, on a autant d'Url que d'actions à traiter.

La page d'accueil [index.jsp] change :


<%@ 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"/>
  • ligne 5 : la page [index.jsp] redirige le client vers l'url [/personne5/do/formulaire], ce qui revient à demander au contrôleur d'exécuter l'action [formulaire].

10.4. Le code des vues

Les vues [formulaire, réponse, erreurs] changent peu. L'unique changement vient du fait que l'action à accomplir n'est plus précisée de la même façon qu'auparavant où elle était définie dans un champ caché nommé [action] des formulaires postés. Maintenant elle est définie dans l'Url cible des formulaires postés, c.a.d. dans l'attribut [action] de la balise <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>
  • ligne [13]: le paramètre [action] du formulaire réapparaît après avoir disparu un certain temps dans les versions précédentes. Pour comprendre la valeur de cet attribut ici, il faut se rappeler que toutes les Url traitées par l'application sont de la forme [/do/action]. Ligne [13], l'attribut [action] a pour valeur une Url relative (ne commençant pas par /). Aussi le navigateur va-t-il la compléter avec l'Url de la page actuellement affichée donc nécessairement une Url de la forme [/do/action]. Le dernier élément va être remplacé par l'Url relative de l'attribut [action] de la balise <form> pour donner l'Url [/do/validationFormulaire] comme cible du POST.
  • le champ caché [action] a disparu

[réponse.jsp] :


...

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

  • ligne [7]: la cible du POST sera [/do/retourFormulaire]
  • le champ caché [action] a disparu dans le formulaire des lignes 7-8.

[erreurs.jsp] :


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

  • ligne [6]: la cible du POST sera [/do/retourFormulaire]
  • le champ caché [action] a disparu dans le formulaire des lignes 6-7.

Le lecteur est invité à tester ces nouvelles vues selon le principe vu dans les versions précédentes.

10.5. Le contrôleur [ServletPersonne]

Le contrôleur [ServletPersonne] de l'application web [/personne5] va traiter les actions suivantes :

demande

origine

traitement

1

[GET /personne5/do/formulaire]

url tapée par l'utilisateur

- envoyer la vue [formulaire] vide

2

[POST

/personne5/do/validationFormulaire]

avec paramètres [txtNom, txtAge]

postés

clic sur le bouton

[Envoyer] de la vue

[formulaire]

- vérifier les valeurs des paramètres [txtNom, txtAge]

- si elles sont incorrectes, envoyer la vue [erreurs(erreurs)]

- si elles sont correctes, envoyer la vue [reponse(nom,age)]

3

[POST

/personne5/do/retourFormulaire]

sans paramètres postés

clic sur le lien [Retour au

formulaire] des vues

[réponse] et [erreurs].

- envoyer la vue [formulaire] pré-remplie avec les dernières valeurs saisies

Le squelette du contrôleur [ServletPersonne] est identique à celui de la version précédente. Nous passons en revue les modifications amenées aux méthodes [doValidationFormulaire, doRetourFormulaire, doGet], les méthodes [init, doInit, doPost] ne changeant pas.

10.5.1. La méthode [doGet]

La méthode [doGet] ne récupère pas l'action à exécuter de la même façon que dans les versions précédentes :

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

        // on vérifie comment s'est passée l'initialisation de la servlet
        if (erreursInitialisation.size() != 0) {
...
        }
        // on récupère la méthode d'envoi de la requête
        String méthode=request.getMethod().toLowerCase();
        // on récupère l'action à exécuter
        String action=request.getPathInfo();
        // action ?
        if(action==null){
            action="/formulaire";
        }
        // exécution action
        if(méthode.equals("get") && action.equals("/formulaire")){
            // démarrage application
            doInit(request,response);
            return;
        }
        if(méthode.equals("post") && action.equals("/validationFormulaire")){
            // validation du formulaire de saisie
            doValidationFormulaire(request,response);
            return;
        }
        if(méthode.equals("post") && action.equals("/retourFormulaire")){
            // retour au formulaire de saisie
            doRetourFormulaire(request,response);
            return;
        }
        // autres cas
        doInit(request,response);
    }
  • ligne 12 : on récupère l'action à exécuter. Elle est de la forme [/action].
  • lignes 18-22 : traitement de l'action [/formulaire] demandée par une requête GET
  • lignes 23-27 : traitement de l'action [/validationFormulaire] demandée par une requête POST
  • lignes 28-32 : traitement de l'action [/retourFormulaire] demandée par une requête POST

10.5.2. La méthode [doValidationFormulaire]

Cette méthode traite la requête n° 2 [POST /personne5/do/validationFormulaire] avec [txtNom, txtAge] dans les éléments postés. Son code est le suivant :

// validation du formulaire
    void doValidationFormulaire(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException{
        // on récupère les paramètres
        String nom = request.getParameter("txtNom");
        String age = request.getParameter("txtAge");
        // qu'on mémorise dans un cookie
        response.addCookie(new Cookie("nom",nom));
        response.addCookie(new Cookie("age",age));
        // vérification des paramètres
        ...
    }

Les nouveautés :

  • la méthode [doValidationFormulaire] envoie en réponse l'une des vues [réponse, erreurs]. Quelle que soit cette réponse, le contrôleur met dedans deux cookies, lignes 8-9. Un cookie est représenté par un objet [Cookie] dont le constructeur admet deux paramètres, la clé du cookie et la valeur associée à celle-ci.
  • ligne 8 : la valeur saisie pour le nom est mise dans un cookie de clé "nom"
  • ligne 9 : la valeur saisie pour l'âge est mise dans un cookie de clé "age"
  • un cookie est ajouté à la réponse HTTP faite au client avec la méthode [response.addCookie]. Cette réponse n'est ici que préparée. Elle ne sera envoyée véritablement que par exécution de la page JSP de la vue envoyée au client.

10.5.3. La méthode [doRetourFormulaire]

Cette méthode traite la requête n° 2 [POST /personne5/do/retourFormulaire] sans éléments postés. Son code est le suivant :

        // affichage formulaire pré-rempli
    void doRetourFormulaire(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        // on récupère les cookies de l'utilisateur
        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++;
                }
            }
        }
        // on prépare le modèle du formulaire
        request.setAttribute("nom",nom);
        request.setAttribute("age",age);
        // on affiche le formulaire
        getServletContext().getRequestDispatcher((String)params.get("urlFormulaire")).forward(
                request, response);
        return;
    }

Les nouveautés :

La méthode [doRetourFormulaire] doit faire afficher un formulaire pré-rempli avec les dernières saisies faites. Dans la version précédente, celles-ci étaient dans la session. Dans celle-ci, on n'utilise plus la session mais des cookies pour mémoriser des éléments entre deux échanges client-serveur. Lorsque le client a demandé la validation du formulaire, il a reçu en réponse la vue [réponse] ou [erreurs] selon les cas, accompagnée de deux cookies libellés " nom " et " age ". Lors du clic sur le lien [Retour au formulaire] de ces deux vues qui provoque un POST sur l'Url [/do/retourFormulaire], le navigateur va renvoyer au serveur les deux cookies qu'il a reçus.

  • lignes 4-18 : on récupère les valeurs des cookies libellés " nom " et " age ". Assez bizarrement, il n'existe pas de méthode permettant d'avoir la valeur d'un cookie à partir de sa clé. Aussi est-on obligé de passer en revue chacun des cookies reçu.
  • ceci fait, les deux valeurs obtenues sont placées dans le modèle de la vue [formulaire] (lignes 20-21) afin que celle-ci les affiche.

10.6. Tests

Lancer ou relancer Tomcat après y avoir intégré le projet Eclipse [personne-mvc-05] puis demander l'url [http://localhost:8080/personne5].