12. Веб-додаток MVC [personne] — версія 7
12.1. Introduction
У цій версії ми припускаємо, що деякі браузери клієнтів можуть блокувати:
- відправку файлів cookie, які надсилає сервер
- виконання коду JavaScript, вбудованого у сторінки HTML, що відображаються
Проте ми хочемо, щоб такі браузери могли користуватися нашим додатком. Пункт 2 повертає нас до версії 2 нашого додатка, оскільки JavaScript почав використовуватися лише з версії 3. У версії 2 додаток працював без JavaScript, тому проблема, описана в пункті 2, вирішена.
Пункт 1 може виявитися складним для вирішення, а може й ні. Версія 6 нашого додатка працювала без файлів cookie. Об’єднавши версії 2 та 6, ми отримуємо бажаний результат. Додамо ще одну вимогу: додаток повинен підтримувати сесію. Це не безглузда вимога. У додатку, де користувачі мають проходити автентифікацію, сервер повинен запам’ятати пару (ідентифікатор / пароль) користувача, щоб йому не доводилося проходити автентифікацію на кожній сторінці, яку він запитує.
До цього моменту ми використовували три способи збереження інформації під час обміну даними між клієнтом і сервером:
- сесія
- файли cookie
- приховані поля.
Варіант 2 можна виключити, оскільки браузер клієнта може заборонити використання файлів cookie.
Рішення 3 — це те, що ми розглядали раніше у версії 6. Його не можна використовувати з міркувань безпеки. Якщо комбінація (логін/пароль) вбудована в кожну сторінку, що надсилається до браузера, це означає, що вона передається мережею під час кожного обміну даними між клієнтом і сервером. Це негативно впливає на безпеку додатка. Тоді можна розглянути можливість використання протоколу HTTPS, який шифрує обмін даними між клієнтом і сервером. Однак його застосування для кожної сторінки додатка збільшить навантаження на сервер.
Можна відмовитися від рішення 1, оскільки воно також базується на файлах cookie. Під час першого обміну даними між клієнтом і сервером сервер надсилає клієнту сесійний токен, який той повертає серверу з кожним новим запитом. Завдяки цьому токену сервер зможе впізнати свого клієнта та надати йому інформацію, яку він запам’ятав під час попереднього обміну. Сесійний токен надсилається сервером у файлі cookie. Браузер, у якому не вимкнено файли cookie, може повернути цей файл cookie під час наступних запитів. Якщо ж файли cookie вимкнено, є інший варіант: сесійний токен можна включити в URL-адресу запиту. Саме це ми зараз побачимо, повернувшись до аналізу файлу [index.jsp] з версії 4:
<%@ 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"/>
Нагадаємо, що рядок 5 вище перенаправляє клієнта на URL-адресу [/personne4/main?jsessionid=XX], де XX є сесійним токеном, як показано на знімку екрана нижче, отриманому після запиту на URL-адресу [http://localhost:8080/personne4]:

Давайте детальніше розглянемо роботу тегу <c:redirect> стосовно токена сеансу. Візьмемо браузер, у якому дозволено використання файлів cookie. Нижче наведено налаштування браузера Firefox:

У [1] ми вмикаємо файли cookie, а в [2] видаляємо ті, що вже існують, щоб почати з відомої ситуації. Потім ми запитуємо URL-адресу [http://localhost:8080/personne4]. Отримуємо таку відповідь:

Початковий запит клієнта HTTP був таким:
Зазначимо лише, що клієнт не надсилає сесійного файлу cookie. Відповідь HTTP, надіслана сервером, має такий вигляд:
- рядок 1: сервер просить клієнта перенаправитися
- рядок 3: сервер надсилає сесійний токен, пов’язаний з атрибутом [JSESSIONID]
- рядок 4: URL-адреса перенаправлення містить сесійний токен. Тег <c:redirect> розмістив його там, оскільки клієнт не надіслав сесійного файлу cookie.
Браузер, якому було наказано перенаправитися, потім надіслав такий запит:
- рядок 1: він запитує URL-адресу перенаправлення, включаючи токен сеансу. Саме тому на скріншоті браузер відображає цю URL-адресу.
- рядок 10: браузер повертає сесійний токен, який йому надіслав сервер під час попереднього обміну даними. Це нормальний режим роботи файлів cookie, коли вони дозволені в клієнтському браузері. Якщо це не так, отримані файли cookie не повертаються.
Сервер відповів на цей другий запит наступним чином:
Він знайшов сторінку, яку йому запитували, і надсилає її. Зауважимо, що він більше не надсилає сесійний токен. Це нормальна робота сесійного токена: сервер надсилає його браузеру лише один раз у вигляді файлу cookie, а браузер потім повертає його з кожним запитом, щоб його впізнали.
Тепер, використовуючи той самий браузер, знову надішлімо запит на URL-адресу [http://localhost:8080/personne4], ввівши її вручну. У результаті отримаємо таку сторінку:

Бачимо, що URL-адреса, яка відображається браузером, більше не містить сесійного токена. Розглянемо перший обмін даними між клієнтом і сервером:
Браузер надіслав такий запит:
Це точно такий самий запит, як і попереднього разу, проте з однією відмінністю: у рядку 10 браузер повертає токен сеансу, який він отримав під час найпершого обміну даними. Знову ж таки, це нормальна робота, якщо файли cookie у браузері активні.
Сервер надіслав таку відповідь:
Він просить клієнта перенаправитися. Оскільки він отримав сесійний токен від клієнта, він продовжує цю сесію і не надсилає нового сесійного токена. З тієї ж причини тег <c:redirect> не включає цей сесійний токен в URL-адресу перенаправлення. Ось чому URL-адреса, показана на скріншоті вище, не містить сесійного токена.
З усього цього слід винести таке правило: тег <c:redirect> включає сесійний токен в URL-адресу перенаправлення лише в тому випадку, якщо клієнт не надіслав заголовок HTTP:
Це правило також діє для тегу <c:url>, з яким ми познайомимося пізніше.
Що відбувається у браузері, в якому вимкнено файли cookie? Давайте спробуємо. Спочатку скинемо налаштування браузера:

У [1] ми вимикаємо файли cookie, а в [2] видаляємо ті, що вже існують, щоб почати з відомої ситуації. Потім ми запитуємо URL-адресу [http://localhost:8080/personne4]. Отримуємо таку відповідь:

Ми отримуємо той самий результат, що й раніше. Однак обмін даними HTTP не є точно таким самим:
- рядки 1–9: запит № 1 від браузера. Він не надсилає сесійний файл cookie.
- рядки 11–17: відповідь сервера, який просить перенаправити браузер на інший URL. Сервер надсилає сесійний файл cookie рядок 13: тег <c:redirect> включив токен у URL перенаправлення рядок 14.
- рядки 19–27: запит № 2 від браузера. Він не повертає сесійний файл cookie, який щойно надіслав йому сервер, оскільки його файли cookie заблоковані.
- рядки 29–33: відповідь сервера. Можна помітити, що, хоча браузер і не надіслав йому сесійний файл cookie, сервер, однак, не запускає нову сесію, як можна було б очікувати. Це видно з того, що він не надсилає заголовок HTTP [Set-Cookie], як це було зроблено у рядку 13. Це означає, що він продовжує попередню сесію. Він зміг відновити її завдяки сесійному токену, присутньому в URL-адресі, яку браузер запросив у рядку 19.
Слід зауважити, що сервер відстежує сесію, отримуючи сесійний токен, надісланий клієнтом, двома можливими способами:
- у заголовку HTTP [Set-Cookie], надісланому клієнтом
- у URL-адресі, яку запитує клієнт
Тепер, використовуючи той самий браузер, знову звернемося до URL-адреси [http://localhost:8080/personne4], ввівши її вручну, як це робилося, коли файли cookie були дозволені. У результаті ми отримаємо таку сторінку:

Результат відрізняється від того, що ми отримували, коли файли cookie були дозволені: токен сеансу міститься в URL-адресі, яку відображає браузер. Роз’яснимо цей результат, не аналізуючи обмін даними HTTP, що відбувся:
[cookies autorisés]
- під час другого запиту за URL-адресою [http://localhost:8080/personne4] клієнтський браузер надіслав сесійний файл cookie, який він отримав від сервера під час першого запиту за цією ж URL-адресою. Отже, тег <c:redirect> не включив сесійний токен до адреси перенаправлення.
[cookies inhibés]
- під час другого запиту за URL-адресою [http://localhost:8080/personne4] браузер клієнта не надсилає сесійний файл cookie, який він отримав від сервера під час першого запиту за цією ж URL-адресою, оскільки його файли cookie заблоковані. Тег <c:redirect> включає токен сеансу в адресу перенаправлення. Саме тому його можна побачити на знімку екрана вище.
Теги <c:redirect> та <c:url> дають змогу включати сесійний токен в URL-адреси. Саме це рішення пропонується тут.
12.2. Проєкт Eclipse
Щоб створити проект Eclipse [mvc-personne-07] для веб-додатка [/personne7], потрібно продублювати проект [mvc-personne-06], дотримуючись процедури, описаної в розділі 6.2.
![]() | ![]() |
12.3. Налаштування веб-додатку [personne7]
Файл web.xml додатка /personne7 має такий вигляд:
<?xml version="1.0" encoding="UTF-8"?>
...
<display-name>mvc-personne-07</display-name>
...
Цей файл ідентичний файлу попередньої версії, за винятком рядка 3, де ім’я для відображення веб-додатка змінилося на [mvc-personne-07]. Головна сторінка [index.jsp] не змінюється.
...
<c:redirect url="/do/formulaire"/>
12.4. Код подання
Види [formulaire, réponse, erreurs] знову стають такими, якими вони були у версії 2, c.a.d, без JavaScript. Однак вони зберігають теги JSTL з останніх версій.
12.4.1. Вигляд [formulaire]

Було видалено кнопки, пов’язані з кодом JavaScript.
[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>
- рядок 14: цільова URL-адреса POST вказана за допомогою тегу <c:url>, щоб у ній містився токен сеансу на випадок, якщо клієнт — це браузер, який не надсилає заголовок HTTP [Cookie].
- У формі є дві кнопки типу [submit]: [Envoyer] (рядок 28) та [Effacer] (рядок 30). Обидві кнопки мають однакову назву: bouton. Під час натискання кнопки POST браузер надішле параметр:
- button=Send, якщо запит POST був ініційований кнопкою [Send]
- button=Очистити, якщо запит 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>
- рядок 24: цільовий 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>
- рядок 18: цільова 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 | |
- рядок 35: дія [/retourFormulaire] виконується за допомогою GET, а не POST, як у попередній версії.
- рядки 70–87: дія [/validationFormulaire] виконується за допомогою POST, що запускається клацанням наодну з кнопок [Envoyer] або [Effacer] у вікні [formulaire]. Метод [doValidationFormulaire] обробляє ці два випадки за допомогою двох різних методів.
- рядки 90–103: метод [doEnvoyer] відповідає методу [doValidationFormulaire] попередньої версії. Введені дані зберігаються в сесії (рядки 96–98), тоді як у попередній версії вони розміщувалися в запиті.
- рядки 58–67: новий метод [doEffacer] повинен відображати порожню форму. Можна було б скористатися методом [doInit], який уже виконує цю роботу. Тут також скористаємося нагодою, щоб видалити елементи [nom, age] із сесії, щоб вона й надалі відображала останній стан форми.
- рядки 50–55: вимагають відображення подання [formulaire] без видимої ініціалізації шаблону цього подання. Цей шаблон насправді складається з елементів [nom, age], які вже є в сесії. Більше нічого робити не потрібно.
12.6. Tests
Запустіть або перезапустіть Tomcat після інтеграції в нього проекту Eclipse [personne-mvc-07], а потім запросіть URL-адресу [http://localhost:8080/personne7] за допомогою браузера, у якому вимкнено файли cookie та видалено ті, що вже існують. Отримаєте таку відповідь:

Вихідний код, отриманий браузером, такий:
У рядку 1 токен сеансу міститься в цільовому URL-адресі POST.
Заповнимо форму та надішлімо її:

Вихідний код, отриманий браузером, виглядає так:
Рядок 3: токен сеансу міститься в цільовому URL-адресі посилання.

