Skip to content

7. تطبيق الويب MVC [person] – الإصدار 3


قراءة موصى بها في [ref1]: الفصل 9


7.1. مقدمة

نقترح إضافة كود جافا سكريبت إلى صفحات HTML التي يتم إرسالها إلى المتصفح. والمتصفح هو الذي يقوم بتنفيذ كود جافا سكريبت المضمن في الصفحة التي يعرضها. وهذه التقنية مستقلة عن تلك التي يستخدمها خادم الويب لإنشاء مستند HTML (Java/servlets/JSP، ASP.NET، ASP، PHP، Perl، Python، ...).

[form.jsp]

ستبدو طريقة العرض التي تم إنشاؤها من هذه الصفحة كما يلي:

Image

تستخدم الأزرار التي تحتوي على نص داخل مربع كود JavaScript مضمن في صفحة HTML:

التسمية
نوع HTML
الوظيفة
إرسال
<submit>
يعمل كزر [إرسال] في الإصدارات السابقة: يرسل القيم المدخلة إلى وحدة التحكم
[إرسال]
<button>
زر جديد – يتحقق من صحة البيانات المدخلة محليًا قبل إرسالها إلى وحدة التحكم
إعادة تعيين
<reset>
يعيد تعيين النموذج إلى الحالة التي استلمه بها المتصفح في البداية
[مسح]
<button>
مسح محتويات حقلَي الإدخال

فيما يلي مثال على كيفية استخدام زري [إرسال] و[مسح]:

سنستخدم أيضًا كود JavaScript للتعامل مع رابط [العودة إلى النموذج] في عرضي [الأخطاء] و[الاستجابة]. لنأخذ عرض [الاستجابة] كمثال:

1

يكمن الاختلاف في عنوان URL المعروض في [1]. في الإصدار السابق، كان:

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

هنا، لم يعد الإجراء جزءًا من عنوان URL لأنه سيتم إرساله عبر طلب POST بدلاً من طلب GET. يعني هذا التغيير أن عنوان URL الوحيد الذي سيعرضه المتصفح سيكون [http://localhost:8080/personne3/main] بغض النظر عن الإجراء المطلوب.

7.2. مشروع Eclipse

لإنشاء مشروع Eclipse [mvc-personne-03] لتطبيق الويب [/personne3]، قم بنسخ مشروع [mvc-personne-02] باتباع الإجراء الموضح في القسم 6.2، الصفحة 78.

7.3. تكوين تطبيق الويب [personne3]

ملف web.xml لتطبيق /personne3 هو كما يلي:


<?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>
    <!--  Mapping ServletPersonne-->
    <servlet-mapping>
        <servlet-name>personne</servlet-name>
        <url-pattern>/main</url-pattern>
    </servlet-mapping>
    <!--  welcome files -->
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

هذا الملف مطابق للملف الموجود في الإصدار السابق باستثناء بعض التفاصيل:

  • السطر 6: تغير اسم العرض لتطبيق الويب إلى [mvc-personne-03]

تمت إزالة المعلمة [urlController]. في الإصدار السابق، كانت تُستخدم لتعيين هدف POST لعرض [form]. في هذا الإصدار الجديد، سيكون الهدف هو السلسلة الفارغة، أي عنوان URL الذي يعرضه المتصفح. وقد أوضحنا أن هذا سيكون دائمًا [http://localhost:8080/personne3/main]، وهو ما نحتاجه لطلب POST من عرض [form].

تتغير الصفحة الرئيسية [index.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">
<%
  response.sendRedirect("/personne3/main");
%>
  • السطر 5: تقوم صفحة [index.jsp] بإعادة توجيه العميل إلى عنوان URL لوحدة التحكم [ServletPersonne] في التطبيق [/personne3].

7.4. عرض الكود

7.4.1. عرض [النموذج]

أصبح هذا العرض كما يلي:

Image

يتم إنشاؤه بواسطة صفحة JSP التالية [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">
 
<%// on récupère les données du modèle
            String nom = (String) session.getAttribute("nom");
            String age = (String) session.getAttribute("age");
%>
 
 
<html>
  <head>
    <title>Personne - formulaire</title>
    <script language="javascript">
        // -------------------------------
        function effacer(){
          // on efface les champs de saisie
          with(document.frmPersonne){
            txtNom.value="";
            txtAge.value="";
          }//with
        }//effacer
        // -------------------------------      
        function envoyer(){
          // vérification des paramètres avant de les envoyer
          with(document.frmPersonne){
            // le nom ne doit pas être vide
            champs=/^\s*$/.exec(txtNom.value);
            if(champs!=null){
              // le nom est vide
              alert("Vous devez indiquer un nom");
              txtNom.value="";
              txtNom.focus();
              // retour à l'interface visuelle
              return;
            }//if
            // l'âge doit être un entier positif
            champs=/^\s*\d+\s*$/.exec(txtAge.value);
            if(champs==null){
              // l'âge est incorrect
              alert("Age incorrect");
              txtAge.focus();
              // retour à l'interface visuelle
              return;
            }//if
            // les paramètres sont corrects - on les envoie au serveur
            submit();
          }//with
        }//envoyer
      </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>
 
 

ما الجديد:

  • السطر 56: النموذج له اسم [frmPersonne]. سيُستخدم هذا الاسم في كود JavaScript. لاحظ عدم وجود السمة [action]، مما يعني أن النموذج [frmPersonne] سيتم إرساله إلى عنوان URL المعروض في المتصفح.
  • السطر 70: زر [Submit] يخدم نفس الغرض الذي يخدمه زر [Envoyer] في الإصدارات السابقة
  • السطر 71: يؤدي النقر على زر [Envoyer] (من النوع [Button]) إلى تنفيذ دالة JavaScript [envoyer] المحددة في السطر 24
  • السطر 72: لم تتغير وظيفة زر [Reset]
  • السطر 73: يؤدي النقر على زر [Clear] (من النوع [Button]) إلى تنفيذ دالة JavaScript [clear] المحددة في السطر 16

قبل التعليق على كود JavaScript، دعونا نستعرض بعض القواعد:

تدير JavaScript الصفحة المعروضة ومحتواها كشجرة من الكائنات التي يكون جذرها هو كائن [document]. قد يحتوي هذا المستند على نموذج واحد أو أكثر. يشير [document.frmPersonne] إلى النموذج المسمى [frmPersonne] المحدد في السطر 56. يحتوي هذا النموذج أيضًا على كائنات. حقول الإدخال هي جزء منه. وبالتالي، يشير [document.frmPersonne.txtName] إلى الكائن الذي يمثل حقل الإدخال HTML المحدد في السطر 60. يحتوي كائن [txtName] على خصائص متنوعة، بما في ذلك خاصية [value]، التي تشير إلى محتوى حقل الإدخال. وبالتالي، يشير [document.frmPersonne.txtName.value] إلى محتوى حقل الإدخال [txtName].

  • الأسطر 16–22: تحدد دالة JavaScript [clear] سلسلة فارغة في حقول الإدخال [txtName، txtAge].
  • الأسطر 24–49: تتحقق دالة JavaScript [submit] من صحة القيم الموجودة في حقول الإدخال [txtName، txtAge] قبل إرسالها. وتستخدم التعبيرات العادية للقيام بذلك.
  • السطر 28: يتحقق مما إذا كانت قيمة حقل الإدخال [txtNom] تتطابق مع النمط /s*، مما يعني صفر أو أكثر من المسافات. إذا كانت الإجابة بنعم، فهذا يعني أن المستخدم لم يدخل اسمًا. إذا كان هناك تطابق مع النمط، فستكون للمتغير `champs` قيمة غير مؤشر null؛ وإلا، فستكون قيمته null.
  • السطر 29: إذا كانت هناك مطابقة مع النمط
  • السطر 30: عرض رسالة للمستخدم
  • السطر 32: نضبط حقل [txtName] على سلسلة فارغة (قد يكون هناك سلسلة من المسافات)
  • السطر 33: يتم وضع المؤشر الوامض على حقل [txtName]
  • السطر 34: يتم إنهاء الدالة. وبالتالي، لا يتم إرسال القيم المدخلة إلى الخادم.
  • الأسطر 38-45: نتبع إجراءً مشابهًا مع حقل [txtAge]
  • السطر 47: إذا وصلنا إلى هذه النقطة، فهذا يعني أن القيم المدخلة صحيحة. ثم نرسل النموذج [frmPersonne] إلى خادم الويب. بعد ذلك، تسير الأمور كما لو كنا قد نقرنا على الزر المسمى [Submit].

7.4.2. طريقة العرض [reponse]

تعرض طريقة العرض هذه القيم التي تم إدخالها في النموذج عندما تكون صالحة:

بالمقارنة مع الإصدار السابق، لا تظهر الميزة الجديدة إلا عند استخدام رابط [العودة إلى النموذج] في عرض [الرد]:

يكمن الاختلاف في عنوان URL المعروض في [1]. في الإصدار السابق، كان العنوان:

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

هنا، لم يعد الإجراء جزءًا من عنوان URL لأنه سيتم إرساله عبر طلب POST بدلاً من طلب GET. صفحة JSP الجديدة [reponse.jsp] هي lqqqaaaaaaaaaqqAAAaaa

كما يلي:


<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
 
 
<%
    // on récupère les données du modèle
  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>
 

ما الجديد:

  • الأسطر 35-37: يحتوي الرابط [العودة إلى النموذج] على جافا سكريبت. يؤدي النقر على هذا الرابط إلى تشغيل كود جافا سكريبت الموجود في السمة [href]. كما رأينا في دراسة [formulaire.jsp]، نعلم أن هذا الكود يرسل قيم الحقول <input> و<select>... في النموذج [frmPersonne].
  • الأسطر 32–34: تحدد النموذج [frmPersonne]. يحتوي هذا النموذج على حقل واحد فقط من النوع <input type="hidden" ...>، أي حقل مخفي. يُستخدم حقل [action] هذا لتمرير اسم الإجراء المطلوب تنفيذه إلى وحدة التحكم، وهو في هذه الحالة [retourFormulaire].

7.4.3. طريقة العرض [errors]

تقوم طريقة العرض هذه بالإبلاغ عن أخطاء الإدخال في النموذج. التغيير الذي تم إجراؤه هو نفسه الذي تم إجراؤه في طريقة العرض [response]:

يكمن الاختلاف في عنوان URL المعروض في [1]. في الإصدار السابق، كان:

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

هنا، لم يعد الإجراء جزءًا من عنوان URL لأنه سيتم إرساله عبر طلب POST بدلاً من طلب GET. صفحة JSP الجديدة [response.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">
<%@ page import="java.util.ArrayList" %>
 
<%
// on récupère les données du modèle
  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");
        }//for
      %>
    </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>

ما الجديد:

  • الأسطر 30-32: الطريقة الجديدة للتعامل مع نقرات الروابط. التفسيرات المقدمة حول هذا الموضوع في دراسة [response.jsp] تنطبق هنا أيضًا.

7.5. اختبار العرض

لاختبار طرق العرض السابقة، نقوم بنسخ صفحات JSP الخاصة بها في مجلد /WebContent/JSP التابع لمشروع Eclipse:

Image

ثم، في مجلد JSP، يتم تعديل الصفحات على النحو التالي:

[form.jsp]:


...
<%
  // -- test : on crée le modèle de la page
  session.setAttribute("nom","tintin");
  session.setAttribute("age","30");
%>
 
<%// on récupère les données du modèle
            String nom = (String) session.getAttribute("nom");
            String age = (String) session.getAttribute("age");
%>

تمت إضافة السطرين 4 و5 لإنشاء النموذج المطلوب للصفحة في السطرين 9 و10.

[reponse.jsp]:


...
<%
  // -- test : on crée le modèle de la page
  request.setAttribute("nom","milou");
  request.setAttribute("age","10");
  request.setAttribute("lienRetourFormulaire","Retour au formulaire");
%>
 
<%
    // on récupère les données du modèle
  String nom=(String)request.getAttribute("nom");
  String age=(String)request.getAttribute("age");
  String lienRetourFormulaire=(String)request.getAttribute("lienRetourFormulaire");
%>
...

تمت إضافة الأسطر 4–6 لإنشاء القالب المطلوب للصفحة في الأسطر 11–13.

[errors.jsp]:


 
<%
  // -- test : on crée le modèle de la page
  ArrayList<String> erreurs1=new ArrayList<String>();
  erreurs1.add("erreur1");
  erreurs1.add("erreur2");
  request.setAttribute("erreurs",erreurs1);
  request.setAttribute("lienRetourFormulaire","Retour au formulaire");
%>
 
<%
// on récupère les données du modèle
  ArrayList erreurs=(ArrayList)request.getAttribute("erreurs"); 
  String lienRetourFormulaire=(String)request.getAttribute("lienRetourFormulaire");
%>

تمت إضافة الأسطر 4-8 لإنشاء النموذج المطلوب للصفحة في الأسطر 13-14.

قم بتشغيل Tomcat إذا لم تكن قد قمت بذلك بالفعل، ثم اطلب عناوين URL التالية:

 

نحصل على عدد المشاهدات المتوقع.

7.6. وحدة التحكم [ServletPersonne]

ستتولى وحدة التحكم [ServletPersonne] الخاصة بتطبيق الويب [/personne3] معالجة الإجراءات التالية:

لا.
الطلب
المصدر
المعالجة
1
[GET /person3/hand]
عنوان URL الذي أدخله المستخدم
- إرسال [نموذج] فارغ
2
[POST /person3/main]
مع المعلمات [txtName,
txtAge، action=validateForm]
المنشورة
انقر على
[إرسال] في
[form]
- تحقق من قيم معلمات [txtName، txtAge]
- إذا كانت غير صحيحة، أرسل عرض [errors(errors)]
- إذا كانت صحيحة، أرسل عرض [response(name,age)]
3
[POST /person3/main]
مع المعلمات
[action=returnForm]
المستضافة
انقر على [العودة إلى
النموذج] المشاهدات
[response] و [errors].
- إرسال نموذج العرض [form] بعد ملئه مسبقًا بأحدث القيم التي تم إدخالها

وبالتالي، لدينا إجراء جديد يجب التعامل معه: [POST /person3/main] مع المعلمة المرسلة [action=returnForm]. بدلاً من الإجراء القديم [GET /person3/main?action=returnForm].

7.6.1. هيكل وحدة التحكم

هيكل وحدة التحكم [ServletPersonne] مطابق لهيكل الإصدار السابق.

package istia.st.servlets.personne;

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

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

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

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

    // empty form display
    void doInit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        ...
    }

    // display pre-filled form
    void doRetourFormulaire(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        ...
    }

    // form validation
    void doValidationFormulaire(HttpServletRequest request,
    ...
    }

    // post
    public void doPost(HttpServletRequest request, HttpServletResponse response)
    ...
    }
}

ما الجديد:

  • السطر 11: لم يعد المصفوف [parameters] يحتوي على المعلمة [urlController]، التي تمت إزالتها من ملف [web.xml].

لم تتغير طرق [init، doValidationFormulaire]. أما طرق [doGet، doInit، doRetourFormulaire] فقد تغيرت قليلاً.

7.6.2. طريقة [doGet]

يجب أن تتعامل طريقة [doGet] مع الإجراء [POST /person3/main] مع المعلمة المرسلة [action=returnForm]:

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

...
        if(méthode.equals("post") && action.equals("retourFormulaire")){
            // back to input form
            doRetourFormulaire(request,response);
            return;
        }
        // other cases
        doInit(request,response);
    }
  • الأسطر 6-9: معالجة الإجراء [POST /person3/main] باستخدام المعلمة المرسلة [action=returnForm]

7.6.3. طريقة [doInit]

تتعامل هذه الطريقة مع الطلب رقم 1 [GET /person3/hand]. وفيما يلي شفرة هذه الطريقة:

            // empty form display
    void doInit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        // retrieve the user's session
        HttpSession session = request.getSession(true);        
        // send the empty form
        session.setAttribute("nom", "");
        session.setAttribute("age", "");
        getServletContext().getRequestDispatcher((String)params.get("urlFormulaire")).forward(
                request, response);
        return;
    }

التغيير هو أن عرض [form] المعروض في السطر 8 لم يعد يحتوي على عنصر [action] في قالبه.

7.6.4. طريقة [doRetourFormulaire]

تقوم هذه الطريقة بمعالجة الطلب رقم 1 [POST /person3/main] مع [action=formSubmit] في العناصر المرسلة. وفيما يلي شفرة هذه الطريقة:

    // display pre-filled form
    void doRetourFormulaire(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        // retrieve the user's session
        HttpSession session = request.getSession(true);        
        // name present in the session?
        String nom = (String) session.getAttribute("nom");
        if (nom == null)
            session.setAttribute("nom", "");
        // age present in the session?
        String age = (String) session.getAttribute("age");
        if (age == null)
            session.setAttribute("age", "");
        // the form is displayed
        getServletContext().getRequestDispatcher((String)params.get("urlFormulaire")).forward(
                request, response);
        return;
    }

التغيير هو أن عرض [form] المعروض في السطر 14 لم يعد يحتوي على عنصر [action] في قالبه.

7.7. الاختبارات

ابدأ أو أعد تشغيل Tomcat بعد دمج مشروع Eclipse [personne-mvc-03] فيه. اطلب عنوان URL [http://localhost:8080/personne3] ثم كرر الاختبارات الموضحة كمثال في القسم 7.1.