Skip to content

10. برنامه وب MVC [personne] – نسخه ۵

10.1. Introduction

در این نسخه، دو تغییر ایجاد کرده‌ایم:

اولین مورد مربوط به نحوه اطلاع‌رسانی کلاینت به سرور در مورد عملی است که می‌خواهد انجام دهد. تا کنون، این مورد با استفاده از پارامتری به نام [action] در درخواست GET یا POST کلاینت مشخص می‌شد. در اینجا، اقدام توسط آخرین عنصر URL درخواست‌شده توسط کلاینت مشخص خواهد شد، همانطور که در دنباله زیر نشان داده شده است:

Image

در [1]، URL‌ای که فرم به آن ارسال شده است [/personne5/do/validationFormulaire] است. این عنصر نهایی URL، یعنی [validationFormulaire]، به کنترل‌کننده امکان می‌دهد تا اقدام مورد نظر را تشخیص دهد. در [2]، POST که توسط لینک [Retour au formulaire] تحریک شده بود، در URL [/personne5/do/retourFormulaire] اجرا شد. در اینجا نیز، آخرین عنصر URL، [retourFormulaire]، به کنترلر می‌گوید که کدام عمل را انجام دهد.

ما این تغییر را معرفی می‌کنیم زیرا این روشی است که توسط محبوب‌ترین فریم‌ورک‌های توسعه وب، مانند Struts یا Spring MVC استفاده می‌شود.

تمام آدرس‌های URL در برنامه به شکل [/personne5/do/action] خواهند بود. فایل [web.xml] برای برنامه [/personne5] نشان می‌دهد که برنامه آدرس‌های URL را به شکل [/do/*] می‌پذیرد:


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

کنترل‌کننده نام عملی را که باید انجام شود به شرح زیر بازیابی خواهد کرد:

         // بازیابی عملی که باید اجرا شود
String action=request.getPathInfo();

متد [getPathInfo] از شیء [request] آخرین عنصر URL درخواست را برمی‌گرداند.

تغییر دوم مربوط به نحوه ذخیره ورودی کاربر بین چرخه‌های درخواست/پاسخ است. در حال حاضر، این اطلاعات در یک جلسه (session) ذخیره می‌شود. این رویکرد می‌تواند معایبی داشته باشد اگر کاربران زیادی وجود داشته باشند و حجم زیادی از داده‌ها برای هر یک از آن‌ها برای ذخیره کردن باشد. در واقع، هر کاربر جلسه شخصی خود را دارد. علاوه بر این، یک جلسه پس از خروج کاربر برای مدتی فعال باقی می‌ماند، مگر اینکه گزینه‌ای برای خروج (log-out) در نظر گرفته شده باشد. بنابراین، ۱۰۰۰ جلسه ۱۰۰۰ بایتی، ۱ مگابایت حافظه را اشغال خواهند کرد. این همچنان یک نیاز متوسط است و برنامه‌های کمی به طور همزمان ۱۰۰۰ جلسه فعال دارند.

با این حال، جایگزین‌هایی برای جلسات وجود دارند که مصرف حافظه کمتری دارند و ارزش آشنایی با آن‌ها را دارد. در اینجا، از روش کوکی استفاده خواهیم کرد. بیایید این را با یک مثال نشان دهیم.


مرحله ۱: کاربر یک فرم را ارسال می‌کند:


این چرخه درخواست/پاسخ منجر به تبادل‌های زیر، HTTP، بین کلاینت و سرور می‌شود:

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

txtNom=pauline&txtAge=18

این یک POST استاندارد است. نکته خاصی برای اشاره در اینجا وجود ندارد، به جز اینکه با وجود اینکه ما قصد استفاده از یک جلسه (session) را نداریم، سرور وب به هر حال یکی ایجاد می‌کند. این موضوع از توکن جلسه (session token) که مرورگر در خط 11 به سرور بازمی‌فرستد و قبلاً از سرور دریافت کرده بود، قابل مشاهده است.

[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

می‌توانیم ببینیم که در خطوط ۳ و ۴، سربرگ‌های HTTP و [Set-Cookie] به مرورگر کلاینت ارسال شده‌اند، یکی برای نام (خط ۳) و دیگری برای سن (خط ۴). مقادیر این کوکی‌ها، مقادیری هستند که در خط 14 در سربرگ‌های POST و [1] بالا ارسال شده‌اند.


مرحله ۲: بازگشت به فرم


Image

این چرخه درخواست/پاسخ منجر به تبادلات زیر HTTP بین کلاینت و سرور می‌شود:

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

در اینجا می‌بینیم که POST با کلیک بر روی لینک [Retour au formulaire] تحریک می‌شود. در خط ۱۱، می‌بینیم که مرورگر کوکی‌هایی را که دریافت کرده ([nom, age, JSESSIONID]) از طریق هدر HTTP ([Cookie]) به سرور بازمی‌فرستد. این نحوه عملکرد کوکی‌ها است: کلاینت کوکی‌هایی را که سرور برایش ارسال کرده، به سرور بازمی‌فرستد. در این مثال، کنترلر مقادیر [pauline, 18] را دریافت می‌کند که باید آن‌ها را در فیلدهای [txtNom, txtAge]ِ ویوی [formulaire] که در [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

در اینجا نکته خاصی برای ذکر نیست، جز این واقعیت که در این پاسخ، سرور هیچ کوکی‌ای ارسال نکرده است. این امر مانع از آن نمی‌شود که مرورگر در تبادل بعدی، تمام کوکی‌هایی را که از سرور دریافت کرده است، بازگرداند، حتی اگر این کار هیچ هدفی نداشته باشد. بنابراین، ما فشار بر حافظه در دسترس سرور را به قیمت افزایش جریان کاراکتر در تبادلات کلاینت/سرور کاهش می‌دهیم.

10.2. پروژه اکلیپس

برای ایجاد پروژه Eclipse [mvc-personne-05] برای برنامه وب [/personne5]، پروژه [mvc-personne-04] را با دنبال کردن رویه‌ای که در بخش 6.2 توضیح داده شده است، کپی کنید.

10.3. پیکربندی برنامه وب [personne5]

فایل web.xml برای برنامه /personne5 به شرح زیر است:


<?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>
    <!--  نگاشت ServletPersonne-->
    <servlet-mapping>
        <servlet-name>personne</servlet-name>
        <url-pattern>/do/*</url-pattern>
    </servlet-mapping>
    <!--  فایل‌های اصلی -->
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

این فایل به جز چند جزئیات با نسخه قبلی یکسان است:

  • خط ۶: نام نمایشی برنامه وب به [mvc-personne-05] تغییر یافته است
  • خط ۱۸: URLهایی که توسط برنامه پردازش می‌شوند به شکل [/do/*] هستند. قبلاً، فقط URL [/main] پردازش می‌شد. اکنون، به اندازه تعداد عملیات‌های قابل پردازش، URL وجود دارد.

صفحه اصلی [index.jsp] تغییر می‌کند:


<%@ 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"/>
  • خط ۵: صفحه [index.jsp] کلاینت را به URL [/personne5/do/formulaire] هدایت می‌کند، که معادل دستور دادن به کنترلر برای اجرای اکشن [formulaire] است.

10.4. کد نما

ویوهای [formulaire, réponse, erreurs] تغییر بسیار کمی داشته‌اند. تنها تغییر این است که اکشن قابل اجرا دیگر به همان شیوه‌ای که قبلاً در یک فیلد مخفی به نام [action] در فرم‌های ارسال‌شده تعریف می‌شد، مشخص نمی‌شود. اکنون این اقدام در URL مقصد فرم‌های ارسال‌شده، c.a.d، در داخل ویژگی [action] تگ <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>
  • خط [13]: پارامتر فرم [action] پس از مدتی غیبت در نسخه‌های قبلی، دوباره ظاهر شده است. برای درک مقدار این ویژگی در اینجا، مهم است به یاد داشته باشید که تمام URLهایی که توسط برنامه پردازش می‌شوند، از شکل [/do/action] هستند. در خط [13]، مقدار ویژگی [action] یک URL نسبی (بدون شروع با /) است. بنابراین مرورگر، URL صفحهٔ در حال نمایش را به آن اضافه می‌کند و در نتیجه یک URL از شکل [/do/action] به دست می‌آید. عنصر آخر با URL نسبیِ ویژگی [action]ِ تگ <form> جایگزین می‌شود، که در نتیجه URL [/do/validationFormulaire] به عنوان هدفِ POST به دست می‌آید.
  • میدان مخفی [action] ناپدید شده است

[réponse.jsp]:


...

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

  • ردیف [7]: هدف POST، [/do/retourFormulaire] خواهد بود
  • میدان مخفی [action] در فرم در خطوط ۷–۸ ناپدید شده است.

[erreurs.jsp]:


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

  • ردیف [6]: هدف POST خواهد بود [/do/retourFormulaire]
  • میدان مخفی [action] در فرم در خطوط 6–7 ناپدید شده است.

از خوانندگان دعوت می‌شود تا این نماهای جدید را با استفاده از رویکرد تشریح‌شده در نسخه‌های قبلی آزمایش کنند.

10.5. کنترل‌کننده [ServletPersonne]

کنترل‌کننده [ServletPersonne] برای برنامه وب [/personne5] اقدامات زیر را مدیریت خواهد کرد:

خیر.
درخواست
مبدأ
در حال پردازش
۱
[GET /personne5/do/formulaire]
URL وارد شده توسط کاربر
- ارسال نمای خالی [formulaire]
۲
[POST
/person5/do/validationFormulaire]
با پارامترهای [txtNom, txtAge]
ارسال شد
با کلیک روی دکمه
[Envoyer] در نما
[formulaire]
- مقادیر پارامتر را بررسی کنید [txtNom, txtAge]
- اگر نادرست باشند، نما [erreurs(erreurs)] را ارسال کنید
- اگر صحیح باشند، نما [reponse(nom,age)] را ارسال کنید
3
[POST
/person5/do/retourFormulaire]
بدون هیچ پارامتری
روی لینک [بازگشت به کلیک کنید
فرم] در نماها
[réponse] و [erreurs].
- ارسال نمای از پیش پرشده [formulaire] با آخرین مقادیر واردشده

قالب کنترلر [ServletPersonne] با نسخه قبلی یکسان است. ما تغییرات اعمال‌شده در متدهای [doValidationFormulaire, doRetourFormulaire, doGet] را بررسی خواهیم کرد، زیرا متدهای [init, doInit, doPost] بدون تغییر باقی مانده‌اند.

10.5.1. روش [doGet]

روش [doGet] اقدام قابل اجرا را به همان شیوه‌ی نسخه‌های قبلی بازیابی نمی‌کند:

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

         // ما بررسی می‌کنیم که inicialization سرویس‌لت چگونه انجام شد
        if (erreursInitialisation.size() != 0) {
...
        }
         // روش مورد استفاده برای ارسال درخواست را بازیابی می‌کند
        String méthode=request.getMethod().toLowerCase();
         // بازیابی اکشن قابل اجرا
        String action=request.getPathInfo();
         // اقدام؟
        if(action==null){
            action="/formulaire";
        }
         // اجرای اکشن
        if(méthode.equals("get") && action.equals("/formulaire")){
             // راه‌اندازی برنامه
            doInit(request,response);
            return;
        }
        if(méthode.equals("post") && action.equals("/validationFormulaire")){
             //اعتبارسنجی فرم ورودی
            doValidationFormulaire(request,response);
            return;
        }
        if(méthode.equals("post") && action.equals("/retourFormulaire")){
             //بازگشت به فرم ورودی
            doRetourFormulaire(request,response);
            return;
        }
         // موارد دیگر
        doInit(request,response);
    }
  • خط ۱۲: اقدام قابل اجرا بازیابی می‌شود. این در قالب [/action] است.
  • خطوط ۱۸–۲۲: پردازش اقدام [/formulaire] که توسط درخواست GET درخواست شده است
  • خطوط ۲۳–۲۷: پردازش اقدام [/validationFormulaire] که توسط درخواست POST درخواست شده است
  • خطوط ۲۸–۳۲: پردازش اقدام [/retourFormulaire] درخواست‌شده توسط درخواست POST

10.5.2. روش [doValidationFormulaire]

این متد درخواست شمارهٔ ۲، [POST /personne5/do/validationFormulaire]، را به همراه [txtNom, txtAge] در آیتم‌های ارسال‌شده پردازش می‌کند. کد آن به شرح زیر است:

//اعتبارسنجی فرم
    void doValidationFormulaire(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException{
         //بازیابی پارامترها
        String nom = request.getParameter("txtNom");
        String age = request.getParameter("txtAge");
         // که در یک کوکی ذخیره می‌شوند
        response.addCookie(new Cookie("nom",nom));
        response.addCookie(new Cookie("age",age));
         // تأیید پارامتر
        ...
    }

ویژگی‌های جدید:

  • متد [doValidationFormulaire] یکی از ویوها [réponse, erreurs] را برمی‌گرداند. صرف‌نظر از پاسخ، کنترلر دو کوکی را در آن تنظیم می‌کند (خطوط ۸–۹). یک کوکی با یک شیء [Cookie] نمایش داده می‌شود که سازند آن دو پارامتر را می‌پذیرد: کلید کوکی و مقدار مرتبط با آن.
  • خط ۸: مقداری که برای نام وارد شده، در کوکی با کلید «name» قرار می‌گیرد.
  • خط ۹: مقداری که برای 'age' وارد شده است در کوکی با کلید 'age' قرار می‌گیرد
  • یک کوکی به پاسخ HTTP که با استفاده از متد [response.addCookie] به کلاینت ارسال می‌شود، اضافه می‌گردد. این پاسخ صرفاً در اینجا در حال آماده‌سازی است. این پاسخ تنها زمانی واقعاً ارسال خواهد شد که صفحه JSP نمای ارسال‌شده به کلاینت اجرا شود.

10.5.3. روش [doRetourFormulaire]

این متد درخواست شماره 2، [POST /personne5/do/retourFormulaire]، را که حاوی هیچ داده‌ی ارسال‌شده‌ای نیست، پردازش می‌کند. کد آن به شرح زیر است:

         //نمایش فرم از پیش پرشده
    void doRetourFormulaire(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
         // بازیابی کوکی‌های کاربر
        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++;
                }
            }
        }
         // آماده‌سازی قالب فرم
        request.setAttribute("nom",nom);
        request.setAttribute("age",age);
         //فرم نمایش داده می‌شود
        getServletContext().getRequestDispatcher((String)params.get("urlFormulaire")).forward(
                request, response);
        return;
    }

ویژگی‌های جدید:

روش [doRetourFormulaire] باید یک فرم از پیش پرشده با جدیدترین ورودی‌های انجام‌شده را نمایش دهد. در نسخه قبلی، این موارد در جلسه (session) ذخیره می‌شدند. در این نسخه، دیگر از جلسه استفاده نمی‌شود؛ در عوض، از کوکی‌ها برای ذخیره داده‌ها بین تبادل‌های کلاینت-سرور استفاده می‌شود. زمانی که کلاینت اعتبارسنجی فرم را درخواست می‌کرد، در پاسخ، بسته به مورد، نمای [réponse] یا [erreurs] را دریافت می‌کرد که با دو کوکی با برچسب‌های «name» و «age» همراه بود. هنگامی که کاربر روی لینک [Retour au formulaire] در هر یک از این دو نما کلیک می‌کند—که یک POST را در URL [/do/retourFormulaire] تحریک می‌کند—مرورگر دو کوکی را که دریافت کرده است به سمت سرور بازمی‌فرستد.

  • خطوط ۴–۱۸: مقادیر کوکی‌های دارای برچسب «name» و «age» بازیابی می‌شوند. به طرز عجیبی، هیچ روشی برای به‌دست‌آوردن مقدار یک کوکی از روی کلید آن وجود ندارد. بنابراین مجبوریم روی هر یک از کوکی‌های دریافت‌شده به‌صورت حلقه‌ای عبور کنیم.
  • پس از انجام این کار، دو مقدار به‌دست‌آمده در قالب نمای [formulaire] (خطوط ۲۰–۲۱) قرار می‌گیرند تا بتواند آن‌ها را نمایش دهد.

10.6. Tests

پس از ادغام پروژه Eclipse با شناسه [personne-mvc-05Tomcat را راه‌اندازی یا مجدداً راه‌اندازی کنید، سپس URL با شناسه [http://localhost:8080/personne5] را درخواست کنید.