Skip to content

10. MVC-Webanwendung [person] – Version 5

10.1. Einführung

In dieser Version nehmen wir zwei Änderungen vor:

Die erste betrifft die Art und Weise, wie der Client dem Server die Aktion mitteilt, die er ausführen möchte. Bislang wurde dies über einen Parameter namens [action] in der GET- oder POST-Anfrage des Clients angegeben. Hier wird die Aktion durch das letzte Element der vom Client angeforderten URL angegeben, wie in der folgenden Sequenz gezeigt:

Image

In 1 lautet die URL, an die das Formular gesendet wurde, [/person5/do/validateForm]. Es ist das letzte Element [validateForm] der URL, das es dem Controller ermöglichte, die auszuführende Aktion zu erkennen. In 2 wurde der durch den Link [Zurück zum Formular] ausgelöste POST an die URL [/person5/do/returnForm] gesendet. Auch hier teilt das letzte Element [returnForm] der URL dem Controller mit, welche Aktion auszuführen ist.

Wir führen diese Änderung ein, da dies die Methode ist, die von den gängigsten Webentwicklungs-Frameworks wie Struts oder Spring MVC verwendet wird.

Alle URLs in der Anwendung werden die Form [/person5/do/action] haben. Die Datei [web.xml] für die Anwendung [/person5] legt fest, dass sie URLs der Form [/do/*] akzeptiert:


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

Der Controller ruft den Namen der auszuführenden Aktion wie folgt ab:

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

Die Methode [getPathInfo] des Objekts [request] gibt das letzte Element der Anfrage-URL zurück.

Die zweite Änderung betrifft die Art und Weise, wie Benutzereingaben zwischen Anfrage- und Antwortzyklen gespeichert werden. Derzeit werden diese Informationen in einer Sitzung gespeichert. Dieser Ansatz kann Nachteile haben, wenn es viele Benutzer gibt und für jeden einzelne große Datenmengen gespeichert werden müssen. Tatsächlich verfügt jeder Benutzer über eine eigene persönliche Sitzung. Darüber hinaus bleibt die Sitzung noch einige Zeit nach dem Abmelden eines Benutzers aktiv, sofern keine Abmeldeoption bereitgestellt wurde. Somit belegen 1.000 Sitzungen mit jeweils 100 Byte 1 MB Speicherplatz. Dies stellt nach wie vor eine moderate Anforderung dar, und nur wenige Anwendungen haben gleichzeitig 1.000 aktive Sitzungen.

Dennoch gibt es Alternativen zu Sitzungen, die weniger Speicherplatz beanspruchen, und es ist gut, diese zu kennen. Hier verwenden wir die Cookie-Methode. Veranschaulichen wir dies anhand eines Beispiels.


Schritt 1: Der Benutzer sendet ein Formular ab:


Dieser Anfrage-Antwort-Zyklus führt zu folgenden HTTP-Austauschen zwischen dem Client und dem Server:

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

Dies ist eine Standard-POST-Anfrage. Hier gibt es nichts Ungewöhnliches zu beachten, außer dass der Webserver, obwohl wir keine Sitzung verwenden werden, dennoch eine erstellt. Dies geht aus dem Sitzungstoken hervor, das der Browser in Zeile 11 an den Server zurücksendet und das er zuvor vom Server erhalten hatte.

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

Wir sehen, dass in den Zeilen 3 und 4 [Set-Cookie]-HTTP-Header an den Browser des Clients gesendet wurden, einer für den Namen (Zeile 3) und einer für das Alter (Zeile 4). Die Werte dieser Cookies sind die Werte, die in Zeile 14 des obigen POST 1 übermittelt wurden.


Schritt 2: Zurück zum Formular


Image

Dieser Anfrage-Antwort-Zyklus führt zu folgenden HTTP-Austauschen zwischen dem Client und dem Server:

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

Hier sehen wir die POST-Anfrage, die durch das Klicken auf den Link [Zurück zum Formular] ausgelöst wurde. In Zeile 11 sehen wir, dass der Browser die empfangenen Cookies [name, age, JSESSIONID] mithilfe des HTTP-Headers [Cookie] an den Server zurücksendet. So funktionieren Cookies. Der Client sendet die Cookies, die der Server ihm gesendet hat, an den Server zurück. In diesem Beispiel erhält der Controller die Werte [pauline, 18], die er in die Felder [txtName, txtAge] der in 2 angezeigten [form]-Ansicht einfügen muss.

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

Hier gibt es nichts Besonderes zu beachten, außer der Tatsache, dass der Server in dieser Antwort keine Cookies gesendet hat. Dies hindert den Browser jedoch nicht daran, beim nächsten Austausch alle Cookies zurückzusenden, die er vom Server erhalten hat, auch wenn dies keinen Zweck erfüllt. Wir reduzieren somit die Belastung des verfügbaren Speichers auf dem Server auf Kosten eines erhöhten Zeichenflusses beim Austausch zwischen Client und Server.

10.2. Das Eclipse-Projekt

Um das Eclipse-Projekt [mvc-personne-05] für die Webanwendung [/personne5] zu erstellen, duplizieren Sie das Projekt [mvc-personne-04], indem Sie die in Abschnitt 6.2 beschriebene Vorgehensweise befolgen.

10.3. Konfigurieren der Webanwendung [personne5]

Die Datei web.xml für die Anwendung /personne5 sieht wie folgt aus:


<?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>
    <!--  welcome files -->
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>
 

Diese Datei ist bis auf wenige Details identisch mit der in der vorherigen Version:

  • Zeile 6: Der Anzeigename der Webanwendung wurde in [mvc-personne-05] geändert
  • Zeile 18: Die von der Anwendung verarbeiteten URLs haben das Format [/do/*]. Zuvor wurde nur die URL [/main] verarbeitet. Nun gibt es so viele URLs, wie es zu verarbeitende Aktionen gibt.

Die Startseite [index.jsp] ändert sich:


<%@ 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"/>
  • Zeile 5: Die Seite [index.jsp] leitet den Client zur URL [/person5/do/form] weiter, was einer Aufforderung an den Controller entspricht, die Aktion [form] auszuführen.

10.4. Der Code der Ansicht

Die Ansichten [form, response, errors] ändern sich kaum. Die einzige Änderung besteht darin, dass die auszuführende Aktion nicht mehr wie zuvor angegeben wird, als sie in einem versteckten Feld namens [action] in übermittelten Formularen definiert war. Nun wird sie in der Ziel-URL der übermittelten Formulare definiert, d. h. im Attribut [action] des Tags <form>:

[form.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>
  • Zeile [13]: Der Parameter [action] des Formulars taucht wieder auf, nachdem er in früheren Versionen eine Zeit lang fehlte. Um den Wert dieses Attributs hier zu verstehen, denken Sie daran, dass alle von der Anwendung verarbeiteten URLs die Form [/do/action] haben. In Zeile [13] hat das Attribut [action] eine relative URL als Wert (die nicht mit / beginnt). Daher ergänzt der Browser diese durch die URL der aktuell angezeigten Seite, die zwangsläufig eine URL der Form [/do/action] ist. Das letzte Element wird durch die relative URL des Attributs [action] des Tags <form> ersetzt, wodurch die URL [/do/validationFormulaire] als POST-Ziel entsteht.
  • Das versteckte [action]-Feld ist verschwunden

[response.jsp]:


...
 
<html>
...
  <body>
      ...
    <form name="frmPersonne" action="retourFormulaire" method="post">
    </form>
    <a href="javascript:document.frmPersonne.submit();">
      ${lienRetourFormulaire}
    </a>
  </body>
</html>
 
  • Zeile [7]: Das POST-Ziel lautet [/do/returnForm]
  • Das versteckte Feld [action] wurde in den Zeilen 7–8 aus dem Formular entfernt.

[errors.jsp]:


...
<html>
...
  <body>
...
    <form name="frmPersonne" action="retourFormulaire" method="post">
    </form>
    <a href="javascript:document.frmPersonne.submit();">
      ${lienRetourFormulaire}
    </a>
  </body>
</html>
 
  • Zeile [6]: Das POST-Ziel lautet [/do/returnForm]
  • Das versteckte Feld [action] wurde in den Zeilen 6–7 aus dem Formular entfernt.

Leser sind eingeladen, diese neuen Ansichten mit dem in früheren Versionen beschriebenen Ansatz zu testen.

10.5. Der Controller [ServletPersonne]

Der [ServletPersonne]-Controller der Webanwendung [/personne5] wird die folgenden Aktionen verarbeiten:

Nr.
Anfrage
Herkunft
Verarbeitung
1
[GET /person5/do/form]
Vom Benutzer eingegebene URL
- die leere [form]-Ansicht senden
2
[POST
/person5/do/formValidation]
mit den Parametern [txtName, txtAge]
gesendet
Klicken Sie auf die
[Absenden]-Schaltfläche im
[Formular]
- Überprüfen Sie die Werte der Parameter [txtName, txtAge]
- wenn sie falsch sind, sende die Ansicht [errors(errors)]
- wenn sie korrekt sind, sende die Ansicht [response(name,age)]
3
[POST
/person5/do/returnForm]
ohne übermittelte Parameter
Klicken Sie auf das Formular [Zurück zum
Formular] aus der
[Antwort] und [Fehler].
- Senden Sie die [Formular]-Ansicht, die mit den zuletzt eingegebenen Werten vorausgefüllt ist

Das Grundgerüst des [ServletPersonne]-Controllers ist identisch mit dem der vorherigen Version. Wir werden die Änderungen an den Methoden [doValidationFormulaire, doRetourFormulaire, doGet] durchgehen; die Methoden [init, doInit, doPost] bleiben unverändert.

10.5.1. Die [doGet]-Methode

Die [doGet]-Methode ruft die auszuführende Aktion nicht mehr auf dieselbe Weise ab wie in früheren Versionen:

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

        // check how the servlet was initialized
        if (erreursInitialisation.size() != 0) {
...
        }
        // retrieve the request sending method
        String méthode=request.getMethod().toLowerCase();
        // retrieve the action to be executed
        String action=request.getPathInfo();
        // action?
        if(action==null){
            action="/formulaire";
        }
        // execution action
        if(méthode.equals("get") && action.equals("/formulaire")){
            // start application
            doInit(request,response);
            return;
        }
        if(méthode.equals("post") && action.equals("/validationFormulaire")){
            // validation of input form
            doValidationFormulaire(request,response);
            return;
        }
        if(méthode.equals("post") && action.equals("/retourFormulaire")){
            // back to input form
            doRetourFormulaire(request,response);
            return;
        }
        // other cases
        doInit(request,response);
    }
  • Zeile 12: Abrufen der auszuführenden Aktion. Sie hat die Form [/action].
  • Zeilen 18–22: Verarbeitung der durch eine GET-Anfrage angeforderten Aktion [/form]
  • Zeilen 23–27: Verarbeitung der durch eine POST-Anfrage angeforderten Aktion [/validateForm]
  • Zeilen 28–32: Verarbeitung der durch eine POST-Anfrage angeforderten Aktion [/returnForm]

10.5.2. Die Methode [doValidationFormulaire]

Diese Methode verarbeitet die Anfrage Nr. 2 [POST /person5/do/validationFormulaire] mit [txtName, txtAge] in den übermittelten Elementen. Der Code lautet wie folgt:

// form validation
    void doValidationFormulaire(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException{
        // parameters are retrieved
        String nom = request.getParameter("txtNom");
        String age = request.getParameter("txtAge");
        // stored in a cookie
        response.addCookie(new Cookie("nom",nom));
        response.addCookie(new Cookie("age",age));
        // parameter verification
        ...
    }

Was ist neu:

  • Die Methode [doValidationFormulaire] gibt eine der Ansichten [response, errors] zurück. Unabhängig von der Antwort setzt der Controller darin zwei Cookies (Zeilen 8–9). Ein Cookie wird durch ein [Cookie]-Objekt dargestellt, dessen Konstruktor zwei Parameter akzeptiert: den Cookie-Schlüssel und den damit verbundenen Wert.
  • Zeile 8: Der für den Namen eingegebene Wert wird in einem Cookie mit dem Schlüssel „name“ gespeichert
  • Zeile 9: Der für das Alter eingegebene Wert wird in einem Cookie mit dem Schlüssel „age“ gespeichert
  • Ein Cookie wird der an den Client gesendeten HTTP-Antwort mithilfe der Methode [response.addCookie] hinzugefügt. Diese Antwort wird hier lediglich vorbereitet. Sie wird erst dann tatsächlich gesendet, wenn die an den Client gesendete JSP-Seite der Ansicht ausgeführt wird.

10.5.3. Die Methode [doRetourFormulaire]

Diese Methode verarbeitet die Anfrage Nr. 2 [POST /person5/do/retourFormulaire] ohne übermittelte Daten. Ihr Code lautet wie folgt:

        // display pre-filled form
    void doRetourFormulaire(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        // retrieve the user's cookies
        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++;
                }
            }
        }
        // prepare the form template
        request.setAttribute("nom",nom);
        request.setAttribute("age",age);
        // the form is displayed
        getServletContext().getRequestDispatcher((String)params.get("urlFormulaire")).forward(
                request, response);
        return;
    }

Was ist neu:

Die Methode [doRetourFormulaire] sollte ein Formular anzeigen, das mit den letzten Eingaben vorausgefüllt ist. In der vorherigen Version wurden diese in der Sitzung gespeichert. In dieser Version verwenden wir die Sitzung nicht mehr, sondern nutzen stattdessen Cookies, um Daten zwischen Client-Server-Austauschvorgängen zu speichern. Wenn der Client eine Formularvalidierung anforderte, erhielt er je nach Fall die Ansicht [response] oder [errors] als Antwort, zusammen mit zwei Cookies mit den Bezeichnungen „name“ und „age“. Wenn der Link [Zurück zum Formular] auf diesen beiden Ansichten angeklickt wird – was eine POST-Anfrage an die URL [/do/retourFormulaire] auslöst –, sendet der Browser die beiden empfangenen Cookies zurück an den Server.

  • Zeilen 4–18: Wir rufen die Werte der Cookies mit den Bezeichnungen „name“ und „age“ ab. Seltsamerweise gibt es keine Methode, um den Wert eines Cookies anhand seines Schlüssels abzurufen. Daher müssen wir alle empfangenen Cookies durchlaufen.
  • Sobald dies geschehen ist, werden die beiden erhaltenen Werte in die [form]-Ansichtsvorlage (Zeilen 20–21) eingefügt, damit diese sie anzeigen kann.

10.6. Testen

Starten oder starten Sie Tomcat neu, nachdem Sie das Eclipse-Projekt [person-mvc-05] darin integriert haben, und rufen Sie dann die URL [http://localhost:8080/personne5] auf.