Skip to content

7. Aplikacja internetowa MVC [personne] – wersja 3


Zalecana lektura w [ref1]: rozdział 9


7.1. Introduction

Proponujemy dodanie kodu JavaScript do stron HTML wysyłanych do przeglądarki. To właśnie przeglądarka wykonuje kod JavaScript osadzony na wyświetlanej stronie. Technologia ta jest niezależna od tej wykorzystywanej przez serwer WWW do generowania dokumentu HTML (Java/serwlety/JSP, ASP.NET, ASP, PHP, Perl, Python, ...).

[formulaire.jsp]

Widok tej strony będzie wyglądał następująco:

Image

Przyciski, których nazwy są otoczone ramką, wykorzystują kod JavaScript osadzony na stronie HTML:

Nazwa
Typ HTML
Funkcja
Submit
<submit>
pełni funkcję przycisku [Envoyer] z poprzednich wersji: przesyła wprowadzone wartości do kontrolera
[Envoyer]
<button>
nowy przycisk – lokalnie sprawdza poprawność wprowadzonych danych przed przesłaniem ich do kontrolera
Rétablir
<reset>
przywraca formularz do stanu, w jakim został pierwotnie odebrany przez przeglądarkę
[Effacer]
<button>
usuwa zawartość obu pól wprowadzania danych

Oto przykład wykorzystania przycisków [Envoyer] i [Effacer]:

Wykorzystamy również kod JavaScript do obsługi linku [Retour au formulaire] w widokach [erreurs] i [réponse]. Weźmy na przykład widok [réponse]:

1

Różnica polega na adresie URL wyświetlanym w widoku [1]. W poprzedniej wersji adres ten brzmiał:

[http://localhost:8080/osoba2/main?action=retourFormulaire]

W tym przypadku akcja nie jest już częścią adresu URL, ponieważ zostanie wysłana przez POST zamiast przez GET. W wyniku tej zmiany jedynym adresem URL wyświetlanym przez przeglądarkę będzie [http://localhost:8080/personne3/main], niezależnie od żądanej akcji.

7.2. Projekt Eclipse

Aby utworzyć projekt Eclipse [mvc-personne-03] dla aplikacji internetowej [/personne3], należy skopiować projekt [mvc-personne-02], postępując zgodnie z procedurą opisaną w paragrafie 6.2 na stronie 78.

7.3. Konfiguracja aplikacji internetowej [personne3]

Plik web.xml aplikacji /personne3 ma następującą treść:


<?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-03</display-name>
    <!--  ServletPersonne -->
    <servlet>
        <servlet-name>personne</servlet-name>
        <servlet-class>
            istia.st.servlets.personne.ServletPersonne
        </servlet-class>
        <init-param>
            <param-name>urlReponse</param-name>
            <param-value>
                /WEB-INF/vues/reponse.jsp
            </param-value>
        </init-param>
        <init-param>
            <param-name>urlErreurs</param-name>
            <param-value>
                /WEB-INF/vues/erreurs.jsp
            </param-value>
        </init-param>
        <init-param>
            <param-name>urlFormulaire</param-name>
            <param-value>
                /WEB-INF/vues/formulaire.jsp
            </param-value>
        </init-param>
        <init-param>
            <param-name>lienRetourFormulaire</param-name>
            <param-value>
                Retour au formulaire
            </param-value>
        </init-param>
    </servlet>
    <!--  Mapowanie ServletPersonne-->
    <servlet-mapping>
        <servlet-name>personne</servlet-name>
        <url-pattern>/main</url-pattern>
    </servlet-mapping>
    <!--  pliki startowe -->
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

Plik ten jest identyczny z plikiem z poprzedniej wersji, z wyjątkiem kilku szczegółów:

  • wiersz 6: nazwa wyświetlana aplikacji internetowej została zmieniona na [mvc-personne-03]

Zniknął parametr [urlControleur]. W poprzedniej wersji służył on do ustalenia docelowego adresu POST dla widoku [formulaire]. W tej nowej wersji docelowym adresem będzie pusty ciąg znaków, c.a.d. adres URL wyświetlany przez przeglądarkę. Wyjaśniliśmy, że będzie to zawsze [http://localhost:8080/personne3/main], czyli ten, którego potrzebujemy dla POST w widoku [formulaire].

Strona główna [index.jsp] zmienia się:


<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<%
  response.sendRedirect("/personne3/main");
%>
  • wiersz 5: strona [index.jsp] przekierowuje klienta na adres URL kontrolera [ServletPersonne] w aplikacji [/personne3].

7.4. Kod widoków

7.4.1. Widok [formulaire]

Widok ten wygląda teraz następująco:

Image

Jest on generowany przez następujące strony: JSP i [formulaire.jsp]:


<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
  pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<%// pobieramy dane z szablonu
            String nom = (String) session.getAttribute("nom");
            String age = (String) session.getAttribute("age");
%>


<html>
  <head>
    <title>Personne - formulaire</title>
    <script language="javascript">
        // -------------------------------
        function effacer(){
          // kasowanie pól wprowadzania danych
          with(document.frmPersonne){
            txtNom.value="";
            txtAge.value="";
          }//z
        }//wyczyść
        // -------------------------------      
        function envoyer(){
          // weryfikacja parametrów przed wysłaniem
          with(document.frmPersonne){
            // nazwa nie może być pusta
            champs=/^\s*$/.exec(txtNom.value);
            if(champs!=null){
              // nazwa jest pusta
              alert("Vous devez indiquer un nom");
              txtNom.value="";
              txtNom.focus();
              // powrót do interfejsu graficznego
              return;
            }//if
             // wiek musi być dodatnią liczbą całkowitą
            champs=/^\s*\d+\s*$/.exec(txtAge.value);
            if(champs==null){
              // wiek jest nieprawidłowy
              alert("Age incorrect");
              txtAge.focus();
              // powrót do interfejsu graficznego
              return;
            }//if
             // parametry są poprawne – wysyłamy je na serwer
            submit();
          }//with
        }//wyślij
      </script>
  </head>
  <body>
    <center>
      <h2>Personne - formulaire</h2>
      <hr>
      <form name="frmPersonne" method="post">
        <table>
          <tr>
            <td>Nom</td>
            <td><input name="txtNom" value="<%= nom %>" type="text" size="20"></td>
          </tr>
          <tr>
            <td>Age</td>
            <td><input name="txtAge" value="<%= age %>" type="text" size="3"></td>
          </tr>
          <tr>
        </table>
        <table>
          <tr>
            <td><input type="submit" value="Submit"></td>
            <td><input type="button" value="[Envoyer]" onclick="envoyer()"></td>
            <td><input type="reset" value="Rétablir"></td>
            <td><input type="button" value="[Effacer]" onclick="effacer()"></td>
          </tr>
        </table>
        <input type="hidden" name="action" value="validationFormulaire">
      </form>
    </center>
  </body>
</html>


Zmiany:

  • wiersz 56: formularz ma nazwę [frmPersonne]. Nazwa ta będzie używana w kodzie JavaScript. Należy zwrócić uwagę na brak atrybutu [action], co powoduje, że formularz [frmPersonne] zostanie wysłany na adres URL wyświetlany przez przeglądarkę.
  • wiersz 70: przycisk [Submit] pełni rolę przycisku [Envoyer] z poprzednich wersji
  • wiersz 71: kliknięcie przycisku [Envoyer] typu [Button] powoduje wykonanie funkcji JavaScript [envoyer] zdefiniowanej w wierszu 24
  • wiersz 72: przycisk [Rétablir] nie zmienił funkcji
  • wiersz 73: kliknięcie przycisku [Effacer] typu [Button] powoduje wykonanie funkcji JavaScript [effacer] zdefiniowanej w wierszu 16

Zanim przejdziemy do omówienia kodu JavaScript, przypomnijmy kilka zasad notacji:

JavaScript obsługuje wyświetlaną stronę i jej zawartość jako drzewo obiektów, którego korzeniem jest obiekt [document]. W tym dokumencie może znajdować się jeden lub więcej formularzy. [document.frmPersonne] oznacza formularz o nazwie [frmPersonne] zdefiniowany w wierszu 56. Formularz ten również zawiera obiekty. Należą do nich pola wprowadzania danych. Tak więc [document.frmPersonne.txtNom] oznacza obiekt obrazu pola wprowadzania danych HTML zdefiniowanego w wierszu 60. Obiekt [txtNom] posiada różne właściwości, w tym właściwość [value], która określa zawartość pola wprowadzania danych. Zatem [document.frmPersonne.txtNom.value] określa zawartość pola wprowadzania danych [txtNom].

  • wiersze 16–22: funkcja JavaScript [effacer] umieszcza pusty ciąg znaków w polach wprowadzania danych [txtNom, txtAge].
  • wiersze 24–49: funkcja JavaScript [envoyer] sprawdza poprawność wartości w polach wprowadzania danych [txtNom, txtAge] przed ich wysłaniem. Wykorzystuje do tego wyrażenia regularne.
  • wiersz 28: sprawdza, czy wartość pola wprowadzania danych [txtNom] odpowiada wzorcowi /s*, co oznacza 0 lub więcej spacji. Jeśli odpowiedź brzmi „tak”, oznacza to, że użytkownik nie podał nazwiska. W przypadku zgodności z wzorcem zmienna champs będzie miała wartość inną niż wskaźnik null, w przeciwnym razie przyjmie wartość null.
  • wiersz 29: jeśli występuje zgodność z szablonem
  • wiersz 30: wyświetla się komunikat dla użytkownika
  • wiersz 32: w polu [txtNom] umieszczamy pusty ciąg znaków (może tam występować ciąg spacji)
  • wiersz 33: ustawia się migający kursor na polu [txtNom]
  • wiersz 34: przerywamy działanie funkcji. W ten sposób na serwerze nie ma pola POST z wprowadzonymi wartościami.
  • wiersze 38–45: podobną procedurę stosuje się w przypadku pola [txtAge]
  • wiersz 47: jeśli dotarliśmy do tego miejsca, oznacza to, że wprowadzone wartości są poprawne. Następnie wysyłamy (submit) formularz [frmPersonne] na serwer WWW. Wszystko przebiega wtedy tak, jakbyśmy kliknęli przycisk o nazwie [Submit].

7.4.2. Widok [reponse]

Ten widok wyświetla wartości wprowadzone w formularzu, o ile są one prawidłowe:

W porównaniu z poprzednią wersją nowość pojawia się dopiero po kliknięciu linku [Retour au formulaire] w widoku [réponse]:

Różnica polega na adresie URL wyświetlanym w widoku [1]. W poprzedniej wersji adres ten brzmiał:

[http://localhost:8080/osoba2/main?action=retourFormulaire]

W tym przypadku akcja nie jest już częścią adresu URL, ponieważ zostanie wysłana przez POST zamiast przez GET. Nowa strona JSP [reponse.jsp] to lqqqaaaaaaaaaqqAAAaaa

następująca:


<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">


<%
    // pobieramy dane z modelu
  String nom=(String)request.getAttribute("nom");
  String age=(String)request.getAttribute("age");
  String lienRetourFormulaire=(String)request.getAttribute("lienRetourFormulaire");
%>


<html>
    <head>
      <title>Personne</title>
  </head>
  <body>
      <h2>Personne - réponse</h2>
    <hr>
    <table>
        <tr>
          <td>Nom</td>
        <td><%= nom %>
      </tr>
        <tr>
          <td>Age</td>
        <td><%= age %>
      </tr>
    </table>      
    <br>
    <form name="frmPersonne" method="post">
      <input type="hidden" name="action" value="retourFormulaire">
    </form>
    <a href="javascript:document.frmPersonne.submit();">
      <%= lienRetourFormulaire %>
    </a>
  </body>
</html>

Nowości:

  • wiersze 35–37: link [Retour au formulaire] zawiera kod JavaScript. Kliknięcie tego linku powoduje wykonanie kodu JavaScript z atrybutu [href]. Jak widzieliśmy podczas analizy [formulaire.jsp], wiemy, że kod ten wysyła wartości pól typu <input>, <select> itp. z formularza [frmPersonne].
  • wiersze 32–34: definiują formularz [frmPersonne]. Formularz ten zawiera tylko jedno pole typu <input type="hidden" ...>, a więc pole ukryte. To pole [action] służy do przekazania kontrolerowi nazwy akcji do wykonania, w tym przypadku [retourFormulaire].

7.4.3. Widok [erreurs]

Ten widok sygnalizuje błędy wprowadzonych danych w formularzu. Wprowadzona zmiana jest taka sama jak w przypadku widoku [réponse]:

Różnica polega na adresie URL wyświetlanym w widoku [1]. W poprzedniej wersji brzmiał on:

[http://localhost:8080/osoba2/main?action=retourFormulaire]

W tym przypadku akcja nie jest już częścią adresu URL, ponieważ zostanie wysłana przez POST zamiast przez GET. Nowa strona JSP [reponse.jsp] jest następująca:


<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<%@ page import="java.util.ArrayList" %>

<%
// pobieramy dane z modelu
  ArrayList erreurs=(ArrayList)request.getAttribute("erreurs"); 
  String lienRetourFormulaire=(String)request.getAttribute("lienRetourFormulaire");
%>


<html>
    <head>
      <title>Personne</title>
  </head>
  <body>
      <h2>Les erreurs suivantes se sont produites</h2>
    <ul>
        <%
          for(int i=0;i<erreurs.size();i++){
            out.println("<li>" + (String) erreurs.get(i) + "</li>\n");
        }//dla
      %>
    </ul>
    <br>
    <form name="frmPersonne" method="post">
      <input type="hidden" name="action" value="retourFormulaire">
    </form>
    <a href="javascript:document.frmPersonne.submit();">
      <%= lienRetourFormulaire %>
    </a>
  </body>
</html>

Nowości:

  • wiersze 30–32: nowe obsługa kliknięcia w link. Wyjaśnienia na ten temat zawarte w analizie pliku [réponse.jsp] mają zastosowanie również w tym przypadku.

7.5. Testy widoków

Aby przeprowadzić testy poprzednich widoków, duplikujemy ich strony JSP w folderze /WebContent/JSP projektu Eclipse:

Image

Następnie w folderze JSP strony są modyfikowane w następujący sposób:

[formulaire.jsp]:


...
<%
  // -- test: tworzymy szablon strony
  session.setAttribute("nom","tintin");
  session.setAttribute("age","30");
%>

<%// pobieramy dane z szablonu
            String nom = (String) session.getAttribute("nom");
            String age = (String) session.getAttribute("age");
%>

Dodano wiersze 4–5 w celu utworzenia szablonu wymaganego przez stronę w wierszach 9–10.

[reponse.jsp]:


...
<%
  // -- test: tworzymy szablon strony
  request.setAttribute("nom","milou");
  request.setAttribute("age","10");
  request.setAttribute("lienRetourFormulaire","Retour au formulaire");
%>

<%
    // pobieramy dane z szablonu
  String nom=(String)request.getAttribute("nom");
  String age=(String)request.getAttribute("age");
  String lienRetourFormulaire=(String)request.getAttribute("lienRetourFormulaire");
%>
...

Wiersze 4–6 zostały dodane w celu utworzenia szablonu wymaganego przez stronę w wierszach 11–13.

[erreurs.jsp]:



<%
  // -- test: tworzymy szablon strony
  ArrayList<String> erreurs1=new ArrayList<String>();
  erreurs1.add("erreur1");
  erreurs1.add("erreur2");
  request.setAttribute("erreurs",erreurs1);
  request.setAttribute("lienRetourFormulaire","Retour au formulaire");
%>

<%
// pobieranie danych z szablonu
  ArrayList erreurs=(ArrayList)request.getAttribute("erreurs"); 
  String lienRetourFormulaire=(String)request.getAttribute("lienRetourFormulaire");
%>

Wiersze 4–8 zostały dodane w celu utworzenia szablonu wymaganego przez stronę w wierszach 13–14.

Uruchom Tomcat, jeśli jeszcze tego nie zrobiłeś, a następnie wywołaj następujące adresy URL:

 

Otrzymujemy oczekiwane widoki.

7.6. Kontroler [ServletPersonne]

Kontroler [ServletPersonne] aplikacji internetowej [/personne3] będzie obsługiwał następujące akcje:

nr
żądanie
źródło
przetwarzanie
1
[GET /personne3/main]
adres URL wpisany przez użytkownika
- wyślij pusty widok [formulaire]
2
[POST /personne3/main]
z parametrami [txtNom,
txtAge, action=validationFormulaire]
wysłane
po kliknięciu przycisku
[Envoyer] w widoku
[formulaire]
- sprawdź wartości parametrów [txtNom, txtAge]
- jeśli są nieprawidłowe, wyślij widok [erreurs(erreurs)]
- jeśli są prawidłowe, wyślij widok [reponse(nom,age)]
3
[POST /personne3/main]
z parametrami
[action=retourFormulaire]
po
kliknięcie linku [Powrót do
formularza] w widokach
[réponse] i [erreurs].
- wysłać widok [formulaire] wstępnie wypełniony najnowszymi wprowadzonymi wartościami

Mamy zatem nową akcję do przetworzenia: [POST /personne3/main] z przesłanym parametrem [action=retourFormulaire], która zastępuje poprzednią akcję [GET /personne3/main?action=retourFormulaire].

7.6.1. Szkielet kontrolera

Szkielet kontrolera [ServletPersonne] jest identyczny jak w poprzedniej wersji.

package istia.st.servlets.personne;

...
import javax.servlet.http.HttpSession;

@SuppressWarnings("serial")
public class ServletPersonne extends HttpServlet {
     // parametry instancji
    private String urlErreurs = null;
    private ArrayList erreursInitialisation = new ArrayList<String>();
    private String[] paramètres={"urlFormulaire","urlReponse","lienRetourFormulaire"};
    private Map params=new HashMap<String,String>();
    ...

     // inicjalizacja
    @SuppressWarnings("unchecked")
    public void init() throws ServletException {
        ...
    }

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

     // wyświetlenie pustego formularza
    void doInit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        ...
    }

     // wyświetlenie wstępnie wypełnionego formularza
    void doRetourFormulaire(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        ...
    }

     // walidacja formularza
    void doValidationFormulaire(HttpServletRequest request,
    ...
    }

     // przesłanie
    public void doPost(HttpServletRequest request, HttpServletResponse response)
    ...
    }
}

Nowości:

  • w wierszu 11 tablica [paramètres] nie zawiera już parametru [urlControleur], który został usunięty z pliku [web.xml].

Metody [init, doValidationFormulaire] pozostają bez zmian. Metody [doGet, doInit, doRetourFormulaire] ulegają niewielkim zmianom.

7.6.2. Metoda [doGet]

Metoda [doGet] musi przetwarzać akcję [POST /personne3/main] z przesłanym parametrem [action=retourFormulaire]:

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

...
        if(méthode.equals("post") && action.equals("retourFormulaire")){
             // powrót do formularza wprowadzania danych
            doRetourFormulaire(request,response);
            return;
        }
         // inne przypadki
        doInit(request,response);
    }
  • wiersze 6–9: przetwarzanie akcji [POST /personne3/main] z przesłanym parametrem [action=retourFormulaire]

7.6.3. Metoda [doInit]

Metoda ta przetwarza żądanie nr 1 [GET /personne3/main]. Jej kod jest następujący:

             // wyświetlenie pustego formularza
    void doInit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
         // pobieranie sesji użytkownika
        HttpSession session = request.getSession(true);        
         // wysyłanie pustego formularza
        session.setAttribute("nom", "");
        session.setAttribute("age", "");
        getServletContext().getRequestDispatcher((String)params.get("urlFormulaire")).forward(
                request, response);
        return;
    }

Nowością jest to, że widok [formulaire] wyświetlany w wierszu 8 nie zawiera już elementu [action] w swoim szablonie.

7.6.4. Metoda [doRetourFormulaire]

Metoda ta przetwarza zapytanie nr 1 [POST /personne3/main], w którym wśród wysłanych elementów znajduje się [action=retourFormulaire]. Jej kod jest następujący:

     // wyświetlenie wstępnie wypełnionego formularza
    void doRetourFormulaire(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
         // pobieranie sesji użytkownika
        HttpSession session = request.getSession(true);        
         // czy w sesji znajduje się imię?
        String nom = (String) session.getAttribute("nom");
        if (nom == null)
            session.setAttribute("nom", "");
         // czy w sesji znajduje się wiek?
        String age = (String) session.getAttribute("age");
        if (age == null)
            session.setAttribute("age", "");
         // wyświetlanie formularza
        getServletContext().getRequestDispatcher((String)params.get("urlFormulaire")).forward(
                request, response);
        return;
    }

Nowością jest to, że widok [formulaire] wyświetlany w wierszu 14 nie zawiera już elementu [action] w swoim szablonie.

7.7. Tests

Uruchom lub ponownie uruchom Tomcat po dodaniu do niego projektu Eclipse [personne-mvc-03]. Wprowadź adres URL [http://localhost:8080/personne3], a następnie powtórz testy przedstawione jako przykład w paragrafie 7.1.