12. برنامه وب MVC [personne] – نسخه ۷
12.1. Introduction
در این نسخه، فرض میکنیم که برخی از مرورگرهای کلاینت ممکن است غیرفعال کرده باشند:
- ارسال مجدد کوکیهای ارسالشده توسط سرور
- اجرای کد جاوااسکریپت جاسازیشده در صفحات نمایشدادهشده HTML
با این وجود، ما میخواهیم این نوع مرورگرها بتوانند از برنامه ما استفاده کنند. نکته ۲ ما را به نسخه ۲ برنامه ما بازمیگرداند، زیرا جاوااسکریپت از نسخه ۳ به بعد معرفی شد. نسخه ۲ برنامه را بدون جاوااسکریپت اجرا میکرد، بنابراین نکته ۲ حل شده است.
مدیریت نکته ۱ ممکن است دشوار باشد یا نباشد. نسخه ۶ برنامه ما بدون کوکیها کار میکرد. با ادغام نسخههای ۲ و ۶، به نتیجه مورد نظر میرسیم. ما قصد داریم یک محدودیت اضافی اضافه کنیم: برنامه باید یک جلسه (session) را مدیریت کند. این یک محدودیت بیمعنی نیست. در برنامهای که کاربران باید احراز هویت شوند، سرور باید نام کاربری و رمز عبور کاربر را ذخیره کند تا مجبور نشود در هر صفحهای که درخواست میکند، دوباره احراز هویت کند.
تا اینجا، ما از سه راهحل برای ذخیرهسازی اطلاعات در طول مبادلات کلاینت–سرور استفاده کردهایم:
- سشن
- کوکیها
- میدانهای مخفی.
راه حل ۲ را میتوان کنار گذاشت، زیرا ممکن است مرورگر کلاینت استفاده از کوکیها را غیرفعال کرده باشد.
راه حل سوم همان چیزی است که در نسخه ۶ استفاده میشود و قبلاً به آن پرداختیم. به دلایل امنیتی نمیتوان از آن استفاده کرد. اگر جفت نام کاربری/رمز عبور در هر صفحهای که به مرورگر ارسال میشود، تعبیه شود، این بدان معناست که این اطلاعات در هر تبادل کلاینت-سرور از طریق شبکه منتقل میشود. این امر برای امنیت برنامه مناسب نیست. بنابراین میتوانیم استفاده از پروتکل HTTPS را که ارتباطات کلاینت و سرور را رمزگذاری میکند، در نظر بگیریم. با این حال، استفاده از آن برای هر صفحه در برنامه، بار کاری سرور را افزایش خواهد داد.
ممکن است بخواهید راهحل اول را به دلیل مبتنی بر کوکی بودن کنار بگذارید. در طول اولین تبادل بین کلاینت و سرور، سرور یک توکن جلسه (session token) را برای کلاینت ارسال میکند، که کلاینت سپس آن را با هر درخواست جدید برای سرور بازمیگرداند. به لطف این توکن، سرور میتواند کلاینت را تشخیص دهد و اطلاعاتی را که در طول یک تبادل قبلی ذخیره کرده بود، در اختیار آن قرار دهد. توکن جلسه توسط سرور در یک کوکی ارسال میشود. یک مرورگر که کوکیها را غیرفعال نکرده باشد میتواند این کوکی را در درخواستهای بعدی به سرور بازگرداند. اگر کوکیها غیرفعال شده باشند، گزینه دیگری وجود دارد: مرورگر میتواند توکن جلسه را در 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="/main"/>
توجه داشته باشید که خط ۵ بالا، کلاینت را به URL [/personne4/main?jsessionid=XX] هدایت میکند، که در آن XX توکن جلسه است، همانطور که در اسکرینشات زیر نشان داده شده است که پس از درخواست URL [http://localhost:8080/personne4] به دست آمده است:

بیایید نگاهی دقیقتر بیندازیم که تگ <c:redirect> در رابطه با توکن جلسه چگونه کار میکند. بیایید یک مرورگر که کوکیها را میپذیرد در نظر بگیریم. در زیر، مرورگر فایرفاکس را پیکربندی میکنیم:

در [1]، کوکیها را فعال میکنیم و در [2] هر کوکی موجود را حذف میکنیم تا از یک وضعیت شناختهشده شروع کنیم. سپس URL [http://localhost:8080/personne4] را درخواست میکنیم. پاسخ زیر را دریافت میکنیم:

درخواست اولیهٔ کلاینت، HTTP، به شرح زیر بود:
شایان ذکر است که کلاینت هیچ کوکی سشن ارسال نمیکند. پاسخ HTTP که توسط سرور ارسال شده است به شرح زیر است:
- خط ۱: سرور به کلاینت دستور میدهد که هدایت (redirect) کند
- خط ۳: سرور یک توکن جلسه مرتبط با ویژگی [JSESSIONID] را ارسال میکند
- خط ۴: URL هدایت شامل توکن جلسه است. تگ <c:redirect> آن را آنجا قرار داده است زیرا کلاینت کوکی جلسه را ارسال نکرده بود.
مرورگر که از آن خواسته شده بود هدایت را انجام دهد، سپس درخواست زیر را ارسال کرد:
- خط ۱۰: مرورگر URL هدایت (redirect) را همراه با توکن جلسه درخواست میکند. به همین دلیل مرورگر این URL را در اسکرینشات نمایش میدهد.
- خط ۱۰: مرورگر توکن جلسه را که سرور در تبادل قبلی برای آن ارسال کرده بود، بازمیفرستد. این همان روشی است که کوکیها معمولاً وقتی در مرورگر کلاینت فعال هستند، کار میکنند. اگر اینطور نباشد، کوکیهای دریافتشده بازنمیگردند.
سرور به این درخواست دوم با موارد زیر پاسخ داد:
صفحه درخواستشده را پیدا کرده و آن را ارسال میکند. توجه داشته باشید که دیگر توکن جلسه را ارسال نمیکند. این روال معمول کارکرد توکنهای جلسه است: آنها یکبار توسط سرور به شکل یک کوکی به مرورگر ارسال میشوند و سپس مرورگر برای شناسایی شدن، آن را با هر درخواست بازمیگرداند.
اکنون، با استفاده از همان مرورگر، بیایید با تایپ دستی آدرس URL [http://localhost:8080/personne4] را دوباره درخواست کنیم. سپس صفحه زیر را دریافت میکنیم:

میتوانیم ببینیم که URL نمایشدادهشده توسط مرورگر دیگر حاوی توکن جلسه نیست. بیایید اولین تبادل کلاینت/سرور را بررسی کنیم:
مرورگر درخواست زیر را ارسال کرد:
این دقیقاً همان درخواست قبلی است، با یک تفاوت: در خط ۱۰، مرورگر توکن جلسه را که در اولین تبادل دریافت کرده بود، بازمیفرستد. بار دیگر، این رفتار در صورتی که کوکیهای مرورگر فعال باشند، طبیعی است.
سرور پاسخ زیر را ارسال کرد:
این به کلاینت دستور میدهد که هدایت (redirect) کند. از آنجایی که یک توکن جلسه (session token) از کلاینت دریافت کرده است، جلسه را ادامه میدهد و توکن جلسه جدیدی ارسال نمیکند. به همین دلیل، تگ <c:redirect> این توکن جلسه را در URL هدایت مجدد قرار نمیدهد. به همین دلیل است که URL نشان داده شده در اسکرینشات بالا حاوی توکن جلسه نیست.
نکته کلیدی از همه اینها، قانون زیر است: تگ <c:redirect> تنها در صورتی توکن جلسه را در URL هدایت مجدد قرار میدهد که کلاینت هدر HTTP را ارسال نکرده باشد:
این قاعده برای تگ <c:url> نیز صدق میکند که بعداً به آن خواهیم پرداخت.
در مورد مرورگری که کوکیها در آن غیرفعال شدهاند چه اتفاقی میافتد؟ بیایید امتحانش کنیم. ابتدا، مرورگر را ریست میکنیم:

در [1]، کوکیها را غیرفعال میکنیم و در [2] هر کوکی موجود را حذف میکنیم تا از یک وضعیت شناختهشده شروع کنیم. سپس URL را در [http://localhost:8080/personne4] درخواست میکنیم. پاسخ زیر را دریافت میکنیم:

ما به همان نتیجه قبلی میرسیم. با این حال، مبادلات در HTTP دقیقاً یکسان نیستند:
- خطوط ۱–۹: اولین درخواست مرورگر. این درخواست کوکی جلسه (session cookie) ارسال نمیکند.
- خطوط ۱۱–۱۷: پاسخ سرور، دستور میدهد مرورگر به یک URL دیگر هدایت شود. در خط ۱۳ یک کوکی جلسه ارسال میکند: تگ <c:redirect> توکن را در URL هدایت در خط ۱۴ گنجانده است.
- خطوط ۱۹–۲۷: درخواست دوم مرورگر. این خط، کوکی جلسه را که به تازگی توسط سرور ارسال شده است بازنمیگرداند زیرا کوکیها غیرفعال هستند.
- خطوط ۲۹–۳۳: پاسخ سرور. میبینیم که اگرچه مرورگر کوکی جلسه را ارسال نکرده است، اما سرور برخلاف انتظار، جلسه جدیدی را آغاز نمیکند. این موضوع از این واقعیت مشهود است که سربرگ HTTP [Set-Cookie] را که در خط ۱۳ ارسال کرده بود، ارسال نمیکند. این بدان معناست که جلسه قبلی را ادامه میدهد. این سرور به لطف توکن جلسهای که در URL درخواستشده توسط مرورگر در خط ۱۹ موجود بود، توانست این جلسه را بازیابی کند.
شایان ذکر است که سرور یک جلسه را با بازیابی توکن جلسهای که توسط کلاینت ارسال شده است، به دو روش ممکن پیگیری میکند:
- در هدر HTTP [Set-Cookie] ارسالشده توسط کلاینت
- در URL درخواستشده توسط کلاینت
اکنون، با استفاده از همان مرورگر، بیایید URL [http://localhost:8080/personne4] را دوباره با تایپ دستی آن درخواست کنیم، همانطور که هنگام فعال بودن کوکیها انجام شد. سپس صفحه زیر را دریافت میکنیم:

این نتیجه با نتیجهای که هنگام فعال بودن کوکیها به دست آمد متفاوت است: توکن جلسه در URL نمایشدادهشده توسط مرورگر گنجانده شده است. بیایید این نتیجه را بدون بررسی مبادلات HTTP که رخ داده است، توضیح دهیم:
[cookies autorisés]
- در طول درخواست دوم برای URL [http://localhost:8080/personne4]، مرورگر کلاینت کوکی سشن را که در طول درخواست اول برای همان URL از سرور دریافت کرده بود، بازگردانده بود. بنابراین تگ <c:redirect> توکن سشن را در آدرس هدایت (redirect) لحاظ نکرده بود.
[cookies inhibés]
- در طول درخواست دوم برای URL [http://localhost:8080/personne4]، مرورگر کلاینت کوکی جلسه را که در طول درخواست اول برای همان URL از سرور دریافت کرده بود، ارسال نمیکند، زیرا کوکیهای آن غیرفعال هستند. بنابراین تگ <c:redirect> توکن جلسه را در URL هدایت شده قرار میدهد. به همین دلیل است که در اسکرینشات بالا مشاهده میشود.
تگهای <c:redirect> و <c:url> امکان درج توکن جلسه در آدرسهای URL را فراهم میکنند. این راهحل پیشنهادی در اینجا است.
12.2. پروژه اکلیپس
برای ایجاد پروژه Eclipse با نام [mvc-personne-07] برای برنامه وب [/personne7]، با دنبال کردن رویهای که در بخش 6.2 توضیح داده شده است، پروژه [mvc-personne-06] را کپی کنید.
![]() | ![]() |
12.3. پیکربندی برنامه وب [personne7]
فایل web.xml برای برنامه /personne7 به شرح زیر است:
<?xml version="1.0" encoding="UTF-8"?>
...
<display-name>mvc-personne-07</display-name>
...
این فایل به جز خط ۳ که در آن نام نمایشی وباپلیکیشن به [mvc-personne-07] تغییر یافته، با نسخه قبلی یکسان است. صفحه اصلی [index.jsp] بدون تغییر باقی میماند.
...
<c:redirect url="/do/formulaire"/>
12.4. کد نما
ویوهای [formulaire, réponse, erreurs] به حالت قبلی خود در نسخهٔ 2، یعنی c.a.d، بدون جاوااسکریپت بازمیگردند. با این حال، آنها برچسبهای JSTL را از نسخههای اخیر حفظ میکنند.
12.4.1. نما [formulaire]

دکمههای مرتبط با کد جاوااسکریپت حذف شدهاند.
[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">
<%@ taglib uri="/WEB-INF/c.tld" prefix="c" %>
<html>
<head>
<title>Personne - formulaire</title>
</head>
<body>
<center>
<h2>Personne - formulaire</h2>
<hr>
<form name="frmPersonne" action="<c:url value="validationFormulaire"/>" 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" name="bouton" value="Envoyer"></td>
<td><input type="reset" value="Rétablir"></td>
<td><input type="submit" name="bouton" value="Effacer"></td>
</tr>
</table>
</form>
</center>
</body>
</html>
- خط ۱۴: URL مقصد برای POST با استفاده از تگ <c:url> نوشته شده است تا توکن جلسه در صورتی که کلاینت مرورگری باشد که هدر HTTP و [Cookie] را ارسال نمیکند، گنجانده شود.
- این فرم دارای دو دکمه از نوع [submit] است: [Envoyer] (خط ۲۸) و [Effacer] (خط ۳۰). هر دو دکمه نام یکسانی دارند: bouton. وقتی دکمه POST کلیک میشود، مرورگر پارامتر را ارسال خواهد کرد:
- button=Submit اگر POST توسط دکمه [Submit] فعال شده باشد
- button=Delete اگر POST توسط دکمه [حذف] تحریک شده باشد
این پارامتر به ما کمک میکند تا اقدام دقیق مورد نظر را مشخص کنیم، زیرا URL [/do/validationFormulaire] اکنون با دو اقدام متمایز مطابقت دارد.
12.4.2. نما [réponse]

[réponse.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">
<%@ taglib uri="/WEB-INF/c.tld" prefix="c" %>
<html>
<head>
<title>Personne</title>
</head>
<body>
<h2>Personne - réponse</h2>
<hr>
<table>
<tr>
<td>Nom</td>
<td>${nom}</td>
</tr>
<tr>
<td>Age</td>
<td>${age}</td>
</tr>
</table>
<br>
<a href="<c:url value="retourFormulaire"/>">${lienRetourFormulaire}</a>
</body>
</html>
- خط ۲۴: URL مقصد برای HREF با استفاده از تگ <c:url> نوشته شده است تا توکن جلسه در صورتی که کلاینت یک مرورگر باشد که هدر HTTP [Cookie] را ارسال نمیکند، گنجانده شود.
12.4.3. نما [erreurs]

[erreurs.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">
<%@ taglib uri="/WEB-INF/c.tld" prefix="c" %>
<html>
<head>
<title>Personne</title>
</head>
<body>
<h2>Les erreurs suivantes se sont produites</h2>
<ul>
<c:forEach var="erreur" items="${erreurs}">
<li>${erreur}</li>
</c:forEach>
</ul>
<br>
<a href="<c:url value="retourFormulaire"/>">${lienRetourFormulaire}</a>
</body>
</html>
- خط ۱۸: URL مقصد برای HREF با استفاده از تگ <c:url> نوشته شده است تا توکن جلسه در صورتی که کلاینت یک مرورگر باشد که هدر HTTP [Cookie] را ارسال نمیکند، گنجانده شود.
از خوانندگان دعوت میشود تا این نماهای جدید را با استفاده از رویکرد تشریحشده در نسخههای قبلی آزمایش کنند.
12.5. کنترلکننده [ServletPersonne]
کنترلر [ServletPersonne] برای برنامه وب [/personne7] به شرح زیر است:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | |
- خط ۳۵: اکشن [/retourFormulaire] توسط یک GET انجام میشود و دیگر مانند نسخه قبلی توسط یک POST انجام نمیشود.
- سطور ۷۰–۸۷: اقدام [/validationFormulaire] توسط POST فراخوانی میشود که به نوبه خود با کلیک روییکی از دکمههای [Envoyer] یا [Effacer] در نمای [formulaire]. متد [doValidationFormulaire] این دو حالت را با دو روش متفاوت مدیریت میکند.
- خطوط ۹۰–۱۰۳: متد [doEnvoyer] معادل متد [doValidationFormulaire] در نسخه قبلی است. دادههای وارد شده در جلسه (session) قرار داده میشوند (خطوط ۹۶–۹۸)، در حالی که در نسخه قبلی در پرسوجو (query) قرار میگرفتند.
- خطوط ۵۸–۶۷: روش جدید [doEffacer] باید یک فرم خالی نمایش دهد. میتوانستیم روش [doInit] را فراخوانی کنیم که قبلاً این کار را انجام میدهد. در اینجا، همچنین از این فرصت استفاده میکنیم تا عناصر [nom, age] را از جلسه پاک کنیم تا همچنان آخرین وضعیت فرم را منعکس کند.
- خطوط ۵۰–۵۵: سیستم را دستور میدهیم تا نمای [formulaire] را بدون هیچگونه inicialization ظاهری برای قالب نما نمایش دهد. این قالب در واقع از عناصر [nom, age] تشکیل شده است که از قبل در جلسه موجود هستند. هیچ اقدام بیشتری لازم نیست.
12.6. Tests
پس از ادغام پروژه Eclipse با شناسه [personne-mvc-07]، Tomcat را راهاندازی یا مجدداً راهاندازی کنید، سپس با استفاده از مرورگری که کوکیها در آن غیرفعال شده و هرگونه کوکی موجود حذف شده است، URL با شناسه [http://localhost:8080/personne7] را درخواست کنید. پاسخ زیر دریافت میشود:

کد منبع دریافتی توسط مرورگر به شرح زیر است:
خط ۱: توکن جلسه در URL مقصد POST قرار دارد.
بیایید فرم را پر کرده و آن را ارسال کنیم:

کد منبع دریافتشده توسط مرورگر به شرح زیر است:
خط ۳: توکن جلسه در URL مقصد لینک قرار دارد.

