Apache FreeMarker – это шаблонизатор для генерации любого вида текстового вывода от HTML до электронной почты и др. Spring Framework имеет встроенную интеграцию для использования Spring MVC с шаблонами FreeMarker.
Конфигурация представления
В следующем примере показано, как настроить FreeMarker в качестве технологии представления:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.freeMarker();
}
// Конфигурируем FreeMarker...
@Bean
public FreeMarkerConfigurer freeMarkerConfigurer() {
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setTemplateLoaderPath("/WEB-INF/freemarker");
return configurer;
}
}
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
override fun configureViewResolvers(registry: ViewResolverRegistry) {
registry.freeMarker()
}
// Конфигурируем FreeMarker...
@Bean
fun freeMarkerConfigurer() = FreeMarkerConfigurer().apply {
setTemplateLoaderPath("/WEB-INF/freemarker")
}
}
В следующем примере показано, как сконфигурировать то же самое на XML:
<mvc:annotation-driven/>
<mvc:view-resolvers>
<mvc:freemarker/>
</mvc:view-resolvers>
<!-- Конфигурируем FreeMarker... -->
<mvc:freemarker-configurer>
<mvc:template-loader-path location="/WEB-INF/freemarker"/>
</mvc:freemarker-configurer>
Кроме того, можно объявить бин FreeMarkerConfigurer
для полного контроля над всеми свойствами, как показано в следующем примере:
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
</bean>
Ваши шаблоны должны храниться в директории, указанной FreeMarkerConfigurer
, как показано в предыдущем примере. Учитывая предыдущую конфигурацию, если ваш контроллер возвращает имя представления welcome
, преобразователь ищет шаблон /WEB-INF/freemarker/welcome.ftl
.
Конфигурация FreeMarker
Можно передать "Settings" и "SharedVariable" обработчика FreeMarker непосредственно в объект Конфигурация
обработчика FreeMarker (который управляется Spring), установив соответствующие свойства бина вFreeMarkerConfigurer
. Для свойства freemarkerSettings
требуется объект java.util.Properties
, а для свойства freemarkerVariables
- java.util.Map
. В следующем примере показано, как использовать FreeMarkerConfigurer
:
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
<property name="freemarkerVariables">
<map>
<entry key="xml_escape" value-ref="fmXmlEscape"/>
</map>
</property>
</bean>
<bean id="fmXmlEscape" class="freemarker.template.utility.XmlEscape"/>
Подробности о настройках и переменных, применимых к объекту Конфигурация
, см. в документации по FreeMarker.
Обработка форм
Spring предусматривает библиотеку тегов для использования в JSP, которая содержит, среди прочего, элемент <spring:bind/>
. Этот элемент в первую очередь позволяет формам выводить на экран значения из базовых объектов формы и отображать результаты неудачных проверок из Validator
на веб-уровне или бизнес-уровне. Spring также имеет поддержку той же функциональности во FreeMarker, с дополнительными удобными макросами для генерации самих элементов ввода формы.
Макросы привязки
Средства поддержки стандартного набора макросов находятся в файле spring-webmvc.jar
для FreeMarker, поэтому они всегда доступны для соответствующим образом настроенного приложения.
Некоторые макросы, определенные в библиотеках шаблонизации в Spring, считаются внутренними (приватными), но в определениях макросов такого ограничения нет, что делает все макросы видимыми для вызывающего кода и пользовательских шаблонов. Следующие разделы посвящены только макросам, которые необходимо вызывать непосредственно из шаблонов. Если вам нужно просмотреть код макроса напрямую, файл называется spring.ftl
и находится в пакете org.springframework.web.servlet.view.freemarker
.
Простое привязывание
В ваших HTML-формах на базе шаблонов FreeMarker, которые действуют как представление формы для контроллера Spring MVC, можно использовать код, подобный тому, что представлен в следующем примере, для привязки к значениям полей и вывода на экран сообщений об ошибках для каждого поля ввода аналогично эквиваленту JSP. В следующем примере показано представление personForm
:
<!-- Макросы FreeMarker должны быть импортированы в пространство имен.
Мы настоятельно рекомендуем придерживаться параметра "spring". -->
<#import "/spring.ftl" as spring/>
<html>
...
<form action="" method="POST">
Name:
<@spring.bind "personForm.name"/>
<input type="text"
name="${spring.status.expression}"
value="${spring.status.value?html}"/><br />
<#list spring.status.errorMessages as error> <b>${error}</b> <br /> </#list>
<br />
...
<input type="submit" value="submit"/>
</form>
...
</html>
<@spring.bind>
требует аргумент "path", который состоит из имени вашего объекта команды (это "command", если вы не изменили его в конфигурации контроллера), за которым следует точка и имя поля объекта команды, к которому вы хотите осуществить привязку. Также можно использовать вложенные поля, например command.address.street
. Макрос bind предусматривает логику HTML-экранирования по умолчанию, заданную параметром defaultHtmlEscape контекста ServletContext
в web.xml.
Альтернативная форма макроса <@spring.bindEscaped>
принимает второй аргумент, который явно указывает, следует ли использовать HTML-экранироваание в сообщениях об ошибке определения статуса или в значениях. Можно установить значение в true
или false
в зависимости от необходимости. Дополнительные макросы для работы с формами упрощают использование HTML-экранирования, и вам стоит использовать эти макросы везде, где это возможно. Они описаны в следующем разделе.
Макросы ввода
Дополнительные вспомогательные макросы для FreeMarker упрощают привязку и генерацию форм (включая вывод на экран ошибок валидации). Нет необходимости использовать эти макросы для создания полей ввода формы, их можно смешивать и сочетать с помощью простого HTML или прямых вызовов макросов привязки Spring, о которых мы рассказывали ранее.
В следующей таблице доступных макросов приведены определения шаблона FreeMarker Template (FTL) и список параметров, которые принимает каждый из них:
макрос | Определение FTL |
---|---|
|
<@spring.message code/> |
|
<@spring.messageText code, text/> |
|
<@spring.url relativeUrl/> |
|
<@spring.formInput path, attributes, fieldType/> |
|
<@spring.formHiddenInput path, attributes/> |
|
<@spring.formPasswordInput path, attributes/> |
|
<@spring.formTextarea path, attributes/> |
|
<@spring.formSingleSelect path, options, attributes/> |
|
<@spring.formMultiSelect path, options, attributes/> |
|
<@spring.formRadioButtons path, options separator, attributes/> |
|
<@spring.formCheckboxes path, options, separator, attributes/> |
|
<@spring.formCheckbox path, attributes/> |
|
<@spring.showErrors separator, classOrStyle/> |
formHiddenInput
и formPasswordInput
фактически нет нужды, так как вы можете использовать обычный макрос formInput
, задав hidden
или password
в качестве значения параметра fieldType
.Параметры любого из вышеперечисленных макросов имеют понятный смысл:
-
path
: имя поля для привязки (т.е. "command.name") -
options
:Map
всех доступных значений, которые можно выбрать в поле ввода. Ключи Map представляют собой значения, которые передаются обратно из формы и привязываются к объекту команды. Объекты Map, хранящиеся наряду с ключами, являются метками, отображаемыми в форме для пользователя, и могут отличаться от соответствующих значений, возвращаемых формой. Обычно такая Map предоставляется контроллером в качестве справочных данных. Вы можете использовать любую реализацию Map в зависимости от требуемой логики работы. Для строго отсортированных Map можно использовать SortedMap (например, TreeMap) с подходящим Comparator, а для произвольных Map, которые должны возвращать значения в порядке вставки, используйте LinkedHashMap или LinkedMap из commons-collections. -
separator
: если несколько опций доступны в виде отдельных элементов (переключатели или флажки), то этот параметр представляет последовательность символов, используемых для разделения каждого из них в списке (например,<br>
). -
attributes
: дополнительная строка произвольных тегов или текста для включения в сам HTML-тег. Эта строка побуквенно повторяется макросом. Например, в полеtextarea
можно передать атрибуты (например, 'rows="5" cols="60"') или информацию о стиле, например, 'style="border:1px solid silver"'. -
classOrStyle
: для макросаshowErrors
– имя класса CSS, который используется в элементеspan,
обертывающем каждую ошибку. Если информация не предоставлена (или значение пустое), ошибки оборачиваются в теги<b></b>
.
В следующих разделах приведены примеры макросов.
Макрос formInput
принимает параметр path(command.name
) и дополнительный параметр attributes
(который в следующем примере пуст). Этот макрос, как и все другие макросы генерации форм, выполняет неявную привязку Spring к параметру "path". Привязка остается действительной до тех пор, пока не будет осуществлена новая привязка, поэтому макросу showErrors
не нужно снова передавать параметр "пути" – он работает с полем, для которого привязка была создана в последний раз.
Макрос showErrors
принимает параметр-разделитель (символы, которые используются для разделения нескольких ошибок в данном поле), а также принимает второй параметр – на этот раз имя класса или атрибут стиля. Обратите внимание, что FreeMarker может задавать значения по умолчанию для параметра "attributes". В следующем примере показано, как использовать макросы formInput
и showErrors
:
<@spring.formInput "command.name"/>
<@spring.showErrors "<br>"/>
В следующем примере показан вывод фрагмента формы с генерацией поля имени и выводом ошибки валидации после того, как форма была отправлена без значения в поле. Валидация происходит через фреймворк Validation в Spring.
Создаваемый HTML похож на следующий пример:
Name:
<input type="text" name="name" value="">
<br>
<b>required</b>
<br>
<br>
Макрос formTextarea
работает так же, как и макрос formInput
, и принимает тот же список параметров. Обычно второй параметр (attributes
) используется для передачи информации о стиле или атрибутов rows
и cols
для textarea
.
Вы можете использовать четыре макроса для полей выбора, чтобы генерировать обычные входные данные выбора значений пользовательского интерфейса в ваших HTML-формах:
-
formSingleSelect
-
formMultiSelect
-
formRadioButtons
-
formCheckboxes
Каждый из четырех макросов принимает Map
опций, которая содержит значение для поля формы и метку, соответствующую этому значению. Значение и метка могут быть одинаковыми.
Следующий пример касается переключателей в FTL. Базовый объект формы задает для этого поля значение по умолчанию "London", поэтому валидация не требуется. Когда форма отображается, весь список городов для выбора предоставляется в качестве справочных данных в модели под именем "cityMap". В следующем листинге показан пример:
...
Town:
<@spring.formRadioButtons "command.address.town", cityMap, ""/><br><br>
В предыдущем листинге отображается строка переключателей, по одному для каждого значения в cityMap
, и используется разделитель ""
. Никаких дополнительных атрибутов не присваивается (последний параметр макроса отсутствует). cityMap
использует одну и ту же String
для каждой пары ключ-значение в Map. Ключи Map – это то, что форма фактически отправляет в качестве параметров POST
-запроса. Значения Map – это метки, которые видит пользователь. В предыдущем примере, учитывая список из трех известных городов и значение по умолчанию в базовом объекте формы, HTML выглядит следующим образом:
Town:
<input type="radio" name="address.town" value="London">London</input>
<input type="radio" name="address.town" value="Paris" checked="checked">Paris</input>
<input type="radio" name="address.town" value="New York">New York</input>
Если ваше приложение предполагает работу с городами по внутренним кодам (например), то можно создать Map кодов с соответствующими ключами, как показано в следующем примере:
protected Map<String, ?> referenceData(HttpServletRequest request) throws Exception {
Map<String, String> cityMap = new LinkedHashMap<>();
cityMap.put("LDN", "London");
cityMap.put("PRS", "Paris");
cityMap.put("NYC", "New York");
Map<String, Object> model = new HashMap<>();
model.put("cityMap", cityMap);
return model;
}
protected fun referenceData(request: HttpServletRequest): Map<String, *> {
val cityMap = linkedMapOf(
"LDN" to "London",
"PRS" to "Paris",
"NYC" to "New York"
)
return hashMapOf("cityMap" to cityMap)
}
Теперь код выводит данные, где значениями переключателя являются соответствующие коды, но пользователь по-прежнему видит более удобные названия городов, как показано ниже:
Town:
<input type="radio" name="address.town" value="LDN">London</input>
<input type="radio" name="address.town" value="PRS" checked="checked">Paris</input>
<input type="radio" name="address.town" value="NYC">New York</input>
HTML-экранирование
Использование по умолчанию макросов формы, описанных ранее, приводит к созданию HTML-элементов, которые соответствуют стандарту HTML 4.01 и используют значение по умолчанию для HTML-экранирования, определенное в вашем файле web.xml
, которое используется средствами поддержки привязки Spring. Чтобы сделать элементы совместимыми с XHTML или переопределить значение HTML-экранирования по умолчанию, можно задать две переменные в вашем шаблоне (или в вашей модели, где их видят ваши шаблоны). Преимущество задания их в шаблонах заключается в том, что их можно изменить на другие значения позже в процессе обработки шаблона, чтобы обеспечить различную логику работы для разных полей вашей формы.
Чтобы переключиться в режим совместимости с XHTML для ваших тегов, задайте значение true
для модели или контекстной переменной с именем xhtmlCompliant
, как показано в следующем примере:
<#-- for FreeMarker -->
<#assign xhtmlCompliant = true>
После обработки этой директивы все элементы, созданные макросами Spring, теперь будут совместимы со стандартом XHTML.
Аналогичным образом можно задать HTML-экранирование для каждого поля, как показано в следующем примере:
<#-- until this point, default HTML escaping is used -->
<#assign htmlEscape = true>
<#-- next field will use HTML escaping -->
<@spring.formInput "command.name"/>
<#assign htmlEscape = false in spring>
<#-- all future fields will be bound with HTML-экранирование off -->