Spring Framework має вбудовану інтеграцію для використання Spring MVC з JSP і JSTL.
Розпізнавачі подання
При розробці JSP зазвичай оголошується бін InternalResourceViewResolver
.
InternalResourceViewResolver
можна використовувати для диспетчеризації до будь-якого ресурсу сервлета, але особливо до JSP. Як найбільш
оптимальний метод рекомендуємо розміщувати файли JSP в директорії під директорією
"WEB-INF",
щоб клієнти не могли отримати до них прямий доступ.
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
JSPs проти JSTL
При використанні стандартної бібліотеки тегів JSP (JSTL) необхідно використовувати
спеціальний клас подання JstlView
, оскільки JSTL вимагає певної підготовки, щоб такі речі як
функції I18N працювали.
Бібліотека тегів JSP для Spring
Spring забезпечує прив'язку даних параметрів запиту до об'єктів команд, як описано у попередніх розділах. Щоб полегшити розробку JSP-сторінок у поєднанні з цими функціями прив'язки даних, Spring передбачає кілька тегів, які спрощують роботу. Всі теги Spring мають функції HTML-екранування, які можна активувати або дезактивувати для екранування символів.
Дескриптор бібліотеки тегів spring.tld
(TLD) входить до pring-webmvc.jar
. Для
отримання вичерпної інформації з окремих тегів переглянь довідник по API або звернися до опису бібліотеки тегів.
Бібліотека тегів форм для Spring
Починаючи з версії 2.0, Spring передбачає повний набір тегів, що підтримують прив'язку даних для роботи з елементами форм під час використання JSP та Spring Web MVC. Кожен тег забезпечує підтримку набору атрибутів відповідного аналога HTML-тега, що робить теги звичними та інтуїтивно зрозумілими у використанні. HTML, що генерується тегами, відповідає стандартам HTML 4.01/XHTML 1.0.
На відміну від інших бібліотек тегів форм/вводів, бібліотека тегів форм для Spring інтегрована зі Spring Web MVC, що дозволяє надати тегам доступ до об'єкта команди та довідкових даних з якими працює твій контролер. Як показано в наступних прикладах, теги форм полегшують розробку, читання та супровід JSP.
Ми пройдемося за тегами форми та розглянемо приклад
використання
кожного тега. Ми включили до складу згенеровані HTML-фрагменти, де певні теги вимагають додаткових коментарів.
Конфігурація Бібліотека тегів форм постачається в комплекті з spring-webmvc.jar
. Дескриптор бібліотеки
називається spring-form.tld
.
Щоб використовувати теги з цієї бібліотеки, додай наступну директиву до верхньої частини твоєї JSP-сторінки:
<%@taglib prefix="form" uri="http: //www.springframework.org/tags/form" %>
де form
— префікс імені тега, який ти хочеш використовувати з цієї бібліотеки.
Тег форми
Цей тег візуалізує HTML-елемент "form" і відкриває шлях
прив'язки до внутрішніх тегів для прив'язування. Він поміщає об'єкт команди у PageContext
, щоб до
об'єкта
команди могли звертатися внутрішні теги. Всі інші теги в цій бібліотеці є вкладеними тегами теги form
.
Припустимо, що у нас є об'єкт предметної області під назвою User
. Це JavaBean з такими властивостями,
як firstName
та lastName
. Можна використовувати його як базовий об'єкт форми нашого
контролера форми, який повертає файл form.jsp
. У цьому прикладі показано, як може виглядати form.jsp
:
<form:form>
<table>
<tr>
<td>First Name:</td>
<td><form:input path="firstName"/></td>
</tr>
<tr>
<td>Last Name:</td>
<td><form:input path="lastName"/></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Save Changes"/>
</td>
</tr>
</table>
</form:form>
Значення firstName
та lastName
витягуються з об'єкта команди, розміщеного у PageContext
контролером сторінки. Продовжуй читати, щоб ознайомитися з більш складними прикладами використання внутрішніх
тегів з тегом form
.
У наступному лістингу показаний згенерований HTML, який виглядає як стандартна форма:
<form method="POST">
<table>
<tr>
<td>First Name:</td>
<td><input name="firstName" type="text" value="Harry"/></td>
</tr>
<tr>
<td>Last Name:</td>
<td><input name="lastName" type="text" value="Potter"/></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Save Changes"/>
</td>
</tr>
</table>
</form>
У попередньому JSP передбачається, що ім'я змінної базового об'єкта форми — command
. Якщо ти
помістиш базовий об'єкт форми до моделі під іншим ім'ям (певно оптимальний варіант), то зможеш прив'язати форму до
іменованої змінної, як показано в наступному прикладі:
<form:form modelAttribute="user">
<table>
<tr>
<td>First Name:</td>
<td><form:input path="firstName"/></td>
</tr>
<tr>
<td>Last Name:</td>
<td><form:input path="lastName"/></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Save Changes"/>
</td>
</tr>
</table>
</form:form>
Тег input
Цей тег візуалізує HTML-елемент input
з прив'язаним значенням та
type='text'
за замовчуванням. Ти також можеш використовувати специфічні для HTML5 типи, такі як email
,
tel
, date
та інші.
Тег checkbox
Цей тег візуалізує HTML-тег
input
з type
, встановленим у checkbox
.
Припустимо, що в нашого User
є такі особисті параметри користувача, як підписка на розсилку новин і список захоплень. У наступному прикладі
показаний клас Preferences
:
public class Preferences {
private boolean receiveNewsletter;
private String[] interests;
private String favouriteWord;
public boolean isReceiveNewsletter() {
return receiveNewsletter;
}
public void setReceiveNewsletter(boolean receiveNewsletter) {
this.receiveNewsletter = receiveNewsletter;
}
public String[] getInterests() {
return interests;
}
public void setInterests(String[] interests) {
this.interests = interests;
}
public String getFavouriteWord() {
return favouriteWord;
}
public void setFavouriteWord(String favouriteWord) {
this.favouriteWord = favouriteWord;
}
}
class Preferences(
var receiveNewsletter: Boolean,
var interests: StringArray,
var favouriteWord: String
)
Відповідний файл form.jsp
може виглядати так:
<form:form>
<table>
<tr>
<td>Subscribe to newsletter?:</td>
<%-- Підхід 1: Властивість має тип java.lang.Boolean --%>
<td><form:checkbox path="preferences.receiveNewsletter"/></td>
</tr>
<tr>
<td>Interests:</td>
<%-- Підхід 2: Властивість є масивом або має тип java.util.Collection --%>
<td>
Quidditch: <form:checkbox path="preferences.interests" value="Quidditch"/>
Herbology: <form:checkbox path="preferences.interests" value="Herbology"/>
Defence Against the Dark Arts: <form:checkbox path="preferences.interests" value="Defence Against the Dark Arts"/>
</td>
</tr>
<tr>
<td>Favourite Word:</td>
<%-- Підхід 3: Властивість має тип java.lang.Object --%>
<td>
Magic: <form:checkbox path="preferences.favouriteWord" value="Magic"/>
</td>
</tr>
</table>
</form:form>
Існує три підходи до тегу checkbox
, які повинні задовольнити всі потреби щодо прапорців.
Підхід перший: Якщо пов'язане значення має тип
java.lang.Boolean
,input(checkbox)
позначається якchecked
, якщо пов'язане значення дорівнюєtrue
. Атрибутvalue
відповідає дозволеному значенню властивостіsetValue(Object)
value.Підхід другий: Якщо пов'язане значення має тип
array
абоjava.util.Collection
,input(checkbox)
позначається якchecked
, якщо конфігуроване значенняsetValue( Object)
присутній у пов'язанійCollection
.Підхід третій: У разі будь-якого іншого типу зв'язаного значення
input(checkbox)
позначається якchecked
, якщо налаштованеsetValue(Object)
дорівнює пов'язаному значенню.
Зверни увагу: незалежно від підходу, генерується та сама HTML-структура. У наступному фрагменті HTML визначено кілька прапорців:
<tr>
<td>Interests:</td>
<td>
Quidditch: <input name="preferences.interests" type="checkbox" value="Quidditch"/>
<input type="hidden" value="1" name="_preferences.interests"/>
Herbology: <input name="preferences.interests" type="checkbox" value="Herbology"/>
<input type="hidden" value="1" name="_preferences.interests"/>
Defence Against the Dark Arts: <input name="preferences.interests" type="checkbox" value="Defence Against the Dark Arts"/>
<input type="hidden" value="1" name="_preferences.interests"/>
</td>
</tr>
Можливо, ти не очікуєш побачити додаткове приховане поле після кожного прапорця. Якщо прапорець на сторінці
HTML не встановлено, його значення не надсилається на сервер як частина параметрів HTTP-запиту після надсилання
форми, тому потрібне обхідне рішення для цієї особливості HTML, щоб прив'язка даних форми Spring працювала. Тег
checkbox
діє згідно до наявної угоди Spring про включення прихованого параметра з префіксом
підкреслення
(_
) для кожного прапорця. Роблячи це, ти фактично кажеш Spring, що "прапорець було видно у формі, і я
хочу, щоб мій об'єкт, до якого прив'язуються дані форми, відображав стан прапорця, незалежно ні від чого".
Тег checkboxes
Цей тег візуалізує кілька HTML-тегів input
з type
,
встановленим у checkbox
.
Цей розділ ґрунтується на прикладі з попереднього розділу про тег
checkbox
. Іноді краще не перераховувати всі можливі хобі на сторінці JSP. Найкраще вказати список
доступних опцій під час виконання та передати його до тега. У цьому полягає призначення тега checkboxes
.
Ти можеш передати Array
, List
або Map
, які містять доступні опції у
властивості items
. Зазвичай пов'язана властивість є колекцією, щоб вона могла містити кілька
значень, обраних користувачем. У цьому прикладі показано сторінку JSP, в якій використовується цей тег:
<form:form>
<table>
<tr>
<td>Interests:</td>
<td>
<%-- Властивість є масивом або має тип java.util.Collection --%>
<form:checkboxes path="preferences.interests" items="${interestList}"/>
</td>
</tr>
</table>
</form:form>
У цьому прикладі передбачається, що interestList
— це List
, доступний як атрибут
моделі, що містить рядки значень вибору. Якщо ти використовуєш Map
, ключ запису Map використовується
як значення, а значення запису Map використовується як позначка, що відображається на екрані. Ти також можеш
використовувати кастомний об'єкт, в якому можна вказати імена властивостей для значення за допомогою
itemValue
та мітки — за допомогою itemLabel
.
Тег radiobutton
Цей тег візуалізує HTML-елемент input
з type
, встановленим у radio
.
Типова схема використання передбачає кілька екземплярів тегів, прив'язаних до однієї й тієї ж властивості, але з різними значеннями, як показано в наступному прикладі:
<tr>
<td>Sex:</td>
<td>
Male: <form:radiobutton path="sex" value="M"/> <br/>
Female: <form:radiobutton path="sex" value="F"/>
</td>
</tr>
Тег radiobuttons
Цей тег візуалізує кілька HTML-елементів input
з type
,
встановленим у radio
.
Як і у випадку з тегом checkboxes
ти можеш передати
доступні опції як змінний час виконання. Для цього можна використовувати тег radiobuttons
. Ти передаєш
Array
, List
або Map
, які містять доступні опції у властивості
items
. Якщо ти використовуєш Map, ключ запису Map використовується як значення, а значення запису Map
— як позначка, що відображається на екрані. Ти також можеш використовувати об'єкт користувача, в якому можна
вказати імена властивостей для значення за допомогою itemValue
і мітки — за допомогою
itemLabel
, як показано в наступному прикладі:
<tr>
<td>Sex:</td>
<td><form:radiobuttons path="sex" items="${sexOptions}"/></td>
</tr>
Тег password
Цей тег візуалізує HTML-тег input
з типом,
встановленим у password
, з прив'язаним значенням.
<tr>
<td>Password:</td>
<td>
<form:password path="password"/>
</td>
</tr>
Зверни увагу, що за замовчуванням значення пароля не відображається. Якщо потрібно, щоб значення пароля
показувалося, можеш встановити значення атрибуту showPassword
у true
, як показано в
наступному прикладі:
<tr>
<td>Password:</td>
<td>
<form:password path="password" value="^76525bvHGq" showPassword="true"/>
</td>
</tr>
Тег select
Цей тег візуалізує HTML-елемент "select". Він підтримує прив'язку даних до
вибраної опції, а також використання вкладених тегів option
та options
.
Припустимо,
що у User
є список навичок. Відповідний HTML може виглядати так:
<tr>
<td>Skills:</td>
<td><form:select path="skills" items="${skills}"/></td>
</tr>
Якщо User
володіє навичками травології, то HTML-джерело рядка "Skills" може виглядати так:
<tr>
<td>Skills:</td>
<td>
<select name="skills" multiple="true">
<option value="Potions">Potions</option>
<option value="Herbology" selected="selected">Herbology</option>
<option value="Quidditch">Quidditch</option>
</select>
</td>
</tr>
Тег option
Цей тег візуалізує HTML-елемент option
. Він встановлює selected
,
ґрунтуючись на пов'язаному значенні. У наступному HTML показане типове виведення для нього:
<tr>
<td>House:</td>
<td>
<form:select path="house">
<form:option value="Gryffindor"/>
<form:option value="Hufflepuff"/>
<form:option value="Ravenclaw"/>
<form:option value="Slytherin"/>
</form:select>
</td>
</tr>
Якщо будинок User
-а знаходиться в Гриффіндорі, то HTML-джерело рядка "House" буде виглядати так:
<tr>
<td>House:</td>
<td>
<select name="house">
<option value="Gryffindor" selected="selected">Gryffindor</option>
<option value="Hufflepuff">Hufflepuff</option>
<option value="Ravenclaw">Ravenclaw</option>
<option value="Slytherin">Slytherin</option>
</select>
</td>
</tr>
- Зверни увагу на додавання
selected
атрибуту.
Тег options
Цей тег візуалізує список HTML-елементів option
. Він встановлює
атрибут selected
на основі зв'язаного значення. У наступному HTML показане типове виведення для нього:
<tr>
<td>Country:</td>
<td>
<form:select path="country">
<form:option value="-" label="--Please Select"/>
<form:options items="${countryList}" itemValue="code" itemLabel="name"/>
</form:select>
</td>
</tr>
Якби User
жив у Великобританії, HTML-джерело рядка "Country" виглядало б наступним чином:
<tr>
<td>Country:</td>
<td>
<select name="country">
<option value="-">--Please Select</option>
<option value="AT">Austria</option>
<option value="UK" selected="selected">United Kingdom</option>
<option value="US">United States</option>
</select>
</td>
</tr>
- Зверни увагу на додавання
selected
атрибуту.
Як показано в попередньому прикладі, комбіноване використання тега option
з тегом
options
створює той же стандартний HTML, але дозволяє явно вказати значення в JSP, яке призначене лише
для виведення на екран (де йому й місце). Скажімо, як у випадку рядка за замовчуванням у прикладі: "-- Please
Select".
Атрибут items
зазвичай заповнюється колекцією чи масивом об'єктів елементів даних.
itemValue
та itemLabel
посилаються на властивості біна цих об'єктів елементів даних, якщо
вони вказані. В іншому випадку самі об'єкти елементів даних перетворюються на рядки. Як варіант, можна зазначити
Map
елементів, і в цьому випадку ключі Map інтерпретуватимуться як значення опцій, а значення Map будуть відповідати
міткам опцій. Якщо також зазначені мітки itemValue
або itemLabel
(або обидві), то
властивість
значення елемента даних буде застосовуватися до ключа Map, а властивість мітки елемента даних — до значення Map.
Тег textarea
Цей тег візуалізує HTML-елемент textarea
. У наступному HTML
показаний типовий результат, що виводиться для нього:
<tr>
<td>Notes:</td>
<td><form:textarea path="notes" rows="3" cols="20"/></td>
<td><form:errors path="notes"/></td>
</tr>
Тег hidden
Цей тег візуалізує HTML-тег input
з type
,
встановленим у hidden
, з прив'язаним значенням. Щоб надіслати незв'язане приховане значення,
використовуй HTML-тег input
з type
, встановленим у hidden
. У наступному
HTML показаний типовий результат, що виводиться для нього:
<form:hidden path="house"/>
Якщо буде вирішено передати значення house
як приховане, то HTML буде виглядати так:
<input name="house" type="hidden" value="Gryffindor"/>
Тег errors
Цей тег візуалізує помилки поля в HTML-елементі span
. Він надає
доступ до помилок, створених у твоєму контролері або помилок, які були створені будь-якими валідаторами, пов'язаними
з твоїм контролером.
Припустимо, що нам потрібно вивести на екран усі повідомлення про помилки для полів
firstName
та lastName
після надсилання форми. У нас є валідатор для екземплярів класу
User
, який називається UserValidator
, як показано в наступному прикладі:
public class UserValidator implements Validator {
public boolean supports(Class candidate) {
return User.class.isAssignableFrom(candidate);
}
public void validate(Object obj, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "required", "Field is required.");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "required", "Field is required.");
}
}
class UserValidator : Validator {
override fun supports(candidate: Class<*>): Boolean {
return User::class.java.isAssignableFrom(candidate)
}
override fun validate(obj: Any, errors: Errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "required", "Field is required.")
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "required", "Field is required.")
}
}
form.jsp
може виглядати так:
<form:form>
<table>
<tr>
<td>First Name:</td>
<td><form:input path="firstName"/></td>
<%-- Show errors for firstName field --%>
<td><form:errors path="firstName"/></td>
</tr>
<tr>
<td>Last Name:</td>
<td><form:input path="lastName"/></td>
<%-- Show errors for lastName field --%>
<td><form:errors path="lastName"/></td>
</tr>
<tr>
<td colspan="3">
<input type="submit" value="Save Changes"/>
</td>
</tr>
</table>
</form:form>
Якщо ми надішлемо форму з порожніми значеннями в полях firstName
та lastName
, то
HTML буде виглядати так:
<form method="POST">
<table>
<tr>
<td>First Name:</td>
<td><input name="firstName" type="text" value=""/></td>
<%-- Associated errors to firstName field displayed --%>
<td><span name="firstName.errors">Field is required.</span></td>
</tr>
<tr>
<td>Last Name:</td>
<td><input name="lastName" type="text" value=""/></td>
<%-- Associated errors to lastName field displayed --%>
<td><span name="lastName.errors">Field is required.</span></td>
</tr>
<tr>
<td colspan="3">
<input type="submit" value="Save Changes"/>
</td>
</tr>
</table>
</form>
А що як ми хочемо відобразити весь список помилок для цієї сторінки? У наступному прикладі показано, що тег
errors
також підтримує деякі базові функції підстановки.
path="*"
: Виводить на екран усі помилки.path="lastName"
: Виводить на екран усі помилки, пов'язані з полемlastName
.Якщо
path
опущено, на екран виводяться лише помилки об'єкта.
У наступному прикладі у верхній частині сторінки відображається список помилок, а поруч із полями — помилки, специфічні для кожного поля:
<form:form>
<form:errors path="*" cssClass="errorBox"/>
<table>
<tr>
<td>First Name:</td>
<td><form:input path="firstName"/></td>
<td><form:errors path="firstName"/></td>
</tr>
<tr>
<td>Last Name:</td>
<td><form:input path="lastName"/></td>
<td><form:errors path="lastName"/></td>
</tr>
<tr>
<td colspan="3">
<input type="submit" value="Save Changes"/>
</td>
</tr>
</table>
</form:form>
HTML буде виглядати так:
<form method="POST">
<span name="*.errors" class="errorBox">Field is required.<br/>Field is required.</span>
<table>
<tr>
<td>First Name:</td>
<td><input name="firstName" type="text" value=""/></td>
<td><span name="firstName.errors">Field is required.</span></td>
</tr>
<tr>
<td>Last Name:</td>
<td><input name="lastName" type="text" value=""/></td>
<td><span name="lastName.errors">Field is required.</span></td>
</tr>
<tr>
<td colspan="3">
<input type="submit" value="Save Changes"/>
</td>
</tr>
</table>
</form>
Дескриптор бібліотеки тегів spring-form.tld
(TLD) включений до spring-webmvc.jar
.
Для отримання вичерпної інформації щодо окремих тегів див. довідник з АРІ або звернися до опису бібліотеки тегів.
Перетворення HTTP-методів
Ключовим принципом REST є використання "уніфікованого інтерфейсу". Це означає, що всіма ресурсами (URL-адресами)
можна маніпулювати за допомогою тих самих чотирьох HTTP-методів: GET, PUT, POST та DELETE. Для кожного методу
специфікація HTTP визначає точну семантику. Наприклад, GET завжди повинен бути безпечною операцією, тобто не мати
побічних ефектів, а PUT або DELETE повинні бути ідемпотентними, тобто ти можеш повторювати ці операції знову і
знову, але кінцевий результат має бути одним і тим самим. Хоча HTTP визначає ці чотири методи, HTML підтримує лише
два
з них: GET та POST. На щастя, є два можливі обхідні шляхи: можна використовувати JavaScript для виконання PUT
або DELETE, або виконати POST зі "справжнім" методом як додатковий параметром (за зразком прихованого поля введення
в
HTML-формі). HiddenHttpMethodFilter
зі Spring задіює цей останній прийом. Цей фільтр є звичайним
фільтром сервлетів, тому його можна використовувати у поєднанні з будь-яким вебфреймворком (не лише Spring MVC).
Додай цей фільтр до твого web.xml, і POST з параметром прихованого method
буде перетворено на запит
відповідного HTTP-метода.
З метою перетворення HTTP-методів тег форми Spring MVC було оновлено для забезпечення підтримки встановлення HTTP-методу. Наприклад, наступний фрагмент взятий з прикладу Pet Clinic:
<form:form method="delete">
<p class="submit"><input type="submit" value="Delete Pet"/></p>
</form:form>
У попередньому прикладі виконується HTTP-метод POST, а "справжній" метод DELETE прихований за параметром
запиту. Він підхоплюється HiddenHttpMethodFilter
, який визначається в web.xml, як показано в наступному
прикладі:
<filter>
<filter-name>httpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>httpMethodFilter</filter-name>
<servlet-name>petclinic</servlet-name>
</filter-mapping>
У наступному прикладі показаний відповідний метод з анотацією @Controller
:
@RequestMapping(method = RequestMethod.DELETE)
public String deletePet(@PathVariable int ownerId, @PathVariable int petId) {
this.clinic.deletePet(petId);
return "redirect:/owners/" + ownerId;
}
@RequestMapping(method = [RequestMethod.DELETE])
fun deletePet(@PathVariable ownerId: Int, @PathVariable petId: Int): String {
clinic.deletePet(petId)
return "redirect:/owners/$ownerId"
}
Теги HTML5
Бібліотека тегів форми Spring дозволяє вводити динамічні атрибути, що означає, що ти можеш вводити будь-які атрибути, специфічні для HTML5.
Тег форми input
підтримує введення атрибута типу, відмінного від text
. Це дозволяє
візуалізувати нові типи даних, специфічних для HTML5, такі як email
, date
,
range
та інші. Зверни увагу, що вводити type='text'
не потрібно, оскільки
text
є типом за замовчуванням.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ