|
|
|
|
|
Quick Links
|
|
|
3.
Разработка View-компонентов
|
|
|
Home
|
|
|
3.1
Обзор
|
|
|
Table of Contents
|
Эта глава посвящена задаче построения View-компонентов
приложения, которые в основном создаются с использованием
технологии JavaServer Pages (JSP). В частности, Struts
обеспечивает поддержку интернационализованных приложений, так же
как и взаимодействие с формами ввода. Также здесь коротко
рассматриваются несколько других разделов, связанных с
View-компонентами.
|
|
Introduction
|
|
Model Components
|
|
View Components
|
|
|
3.2
Интернационализация
|
|
|
Controller
Components
|
Несколько лет назад разработчики приложений могли
рассчитывать на распространение своих приложений только среди
жителей собственной страны, которые используют один (иногда два)
языка и единственный способ представления таких вещей, как даты,
числовые и денежные значения. Однако взрывной рост разработки
приложений с использованием web-технологий, также как и
распространение этих приложений через Интернет, частично сделал
национальные границы невидимыми. Это привело к необходимости
разрабатывать приложения с поддержкой
интернационализации
(часто интернационализацию по-английски называют i18n (ай-эйтин),
потому что в слове internationalization 18 букв между i и
n) и
локализации
(localization).
Struts строится на основе платформы Java, чтобы
обеспечить возможности построения интернационализованных и
локализованных приложений. Ниже приведены ключевые концепции, с
которыми нужно познакомиться:
-
Locale –
основной Java класс, который поддерживает интернационализацию -
java.util.Locale.
Каждая Locale представляет собой конкретный выбор страны и языка
(плюс возможный вариант языка), а также набор способов
форматирования таких вещей как числа и даты.
-
ResourceBundle
- Класс
java.util.ResourceBundle
обеспечивает основные инструменты для поддержки сообщений
на многих языках. Подробнее смотрите Javadocs – класс
ResourceBundle, а также информацию об интернационализации в
документации к вашему релизу JDK.
-
PropertyResourceBundle
– Одна из стандартных реализаций ResourceBundle
позволяет вам определять ресурсы, используя тот же синтаксис
“имя=значение” ("name=value"), что
используется при инициализации файлов свойств. Это очень удобно
для подготовки пакетов ресурсов (resource bundles) с сообщениями,
которые используются в web-приложении, потому что обычно такие
сообщения представлены в виде текста.
-
MessageFormat -
Класс
java.text.MessageFormat
позволяет заменять части строки сообщения (в этом случае
они извлекаются из пакета ресурсов) аргументами, определяемыми во
время выполнения. Это полезно в случае, когда вы создаете
предложение, но слова в нем будут появляться в различном порядке
на различных языках. Строка-заменитель {0} в сообщении заменяется
первым аргументом, {1} заменяется вторым аргументом и так далее.
-
MessageResources – Класс Struts
org.apache.struts.util.MessageResources
позволяет обращаться с пакетами ресурсов как с базой данных, и
позволяет запрашивать конкретную строку сообщения для конкретной
Locale (обычно Locale для текущего пользователя) вместо Locale,
используемой по умолчанию на сервере, где запущено данное
приложение.
Обратите внимание, что поддержка i18n в средах вроде
Struts ограничена представлением интернационализованного
текста и изображений для пользователя. Поддержка для Locale
соответствующих методов ввода (используемых в таких языках
как японский, китайский и корейский) возлагается на клиентское
устройство – обычно web-браузер.
Что получить интернационализованное приложение,
следуйте рекомендациям, описанным в документе по
интернационализации в документации JDK на вашей платформе, для
того чтобы создать файл свойств (properties file), содержащий
сообщения для каждого языка. Пример рассмотрен чуть ниже.
Предположим, что ваш исходный код создается в package
com.mycompany.mypackage,
и таким образом сохраняется в каталоге (относительно вашего
каталога исходных кодов)
com/mycompany/mypackage.
Чтобы создать пакет ресурсов, названный
com.mycompany.mypackage.MyResources,
вы должны создать следующие файлы в каталоге
com/mycompany/mypackage:
-
MyResources.properties
– содержит сообщения на языке, используемом по умолчанию на
вашем сервере. Если ваш язык по умолчанию – English, то в
этом файле должны содержатся строки вроде этой:
prompt.hello=Hello
-
MyResources_xx.properties
– содержит те же самые сообщения на языке, чей ISO код =
“xx” (вы найдете ссылку на список текущих языков на
странице ResourceBundle Javadoc). Для французской версии
сообщения, показанного выше, вы должны написать что то вроде:
prompt.hello=Bonjour. Вы можете иметь пакеты ресурсов для
каждого языка, который вам нужно поддерживать.
Когда вы конфигурируете сервлет-контроллер в
дескрипторе для распространения web-приложения (web application
deployment descriptor), одна из тех вещей, которые вы должны
определить в параметрах инициализации – основное имя пакета
ресурсов для вашего приложения. В случае, описанном выше, это
должен быть com.mycompany.mypackage.MyResources:
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>application</param-name>
<param-value>com.mycompany.mypackage.MyResources</param-value>
</init-param>
<.../>
</servlet>
Важно отметить, что пакет ресурсов должен находиться в class path
для вашего приложения. Другой подход – сохранять
MyResources.properties в каталоге классов вашего приложения.
Затем вы можете просто определить "myResources" как
значение в вашем приложении (as the application value). Просто
будьте осторожны, чтобы этот файл не был удален вашим
build-скриптом, который удаляет классы как часть "clean"
target.
Если вы пользуетесь таким методом, вот Ant task,
который нужно запустить во время компиляции вашего приложения,
который скопирует содержимое каталога src/conf в каталог
классов:
<!-- Copy any configuration files -->
<copy todir="classes">
<fileset dir="src/conf"/>
</copy>
|
|
Resources
|
|
Who We Are
|
|
|
|
3.3
Взаимодействие Forms и FormBean
|
|
Так или иначе, большинство разработчиков
веб-приложений должны создавать формы, используя стандартные
возможности HTML, такие как тэг <input>. Пользователи
ожидают, что интерактивные приложения должны иметь определенное
поведение, и одно из этих ожиданий связано с обработкой ошибок –
если пользователь делает ошибку, приложение должно позволить ему
исправить лишь то, что нужно – без необходимости вводить
заново остальную (правильную) часть информации на текущей странице
или форме.
Выполнение этого требования пользователей является
скучным и громоздким делом, когда мы кодируем лишь с помощью
стандартного HTML и JSP. Например, элемент ввода для поля username
в JSP должен выглядеть примерно так:
<input type="text" name="username"
value="<%= loginBean.getUsername() %>"/>
это трудно правильно напечатать, смущает HTML разработчиков,
которые не опытны в концепциях программирования , и могут привести
к проблемам с редакторами HTML. Вместо этого Struts обеспечивает
исчерпывающий набор средств для создания форм, основываясь на
средствах специальной библиотеки тэгов (Custom Tag Library) в JSP
1.1. Пример, показанный раньше, может быть представлен средствами
Struts следующим образом:
<html:text property="username"/>
без всякой необходимости явно ссылаться на JavaBean, из которого
извлекается первоначально значение. Это автоматически
обрабатывается средой.
HTML-формы иногда используются для загрузки других
файлов (upload other files). Большинство браузеров поддерживают
это с помощью элемента <input type="file">,
который генерирует кнопку поиска файлов (file browse button), но
на совести разработчика остается то, как обработать вхлдящие
файлы. Struts обрабатывает такие "multipart"-формы таким
же способом, как и обычные формы. В следующем разделе мы
рассмотрим использование Struts для создания простой формы входа
(login form), а также простую mulitpart-форму.
|
|
|
3.3.1
Разработка Forms в Struts
|
|
Полный пример login-формы иллюстрирует то, что Struts
взаимодействует с формами гораздо менее болезненно, чем при прямом
использовании HTML и стандартных средств JSP. Рассмотрим следующую
страницу logon.jsp (основанную на примере приложения, включенном в
поставку Struts):
<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<html:html>
<head>
<title>
<bean:message key="logon.title"/>
</title>
<body bgcolor="white">
<html:errors/>
<html:form action="/logon" focus="username">
<table border="0" width="100%">
<tr>
<th align="right">
<html:message key="prompt.username"/>
</th>
<td align="left">
<html:text property="username" size="16"/>
</td>
</tr>
<tr>
<th align="right">
<html:message key="prompt.password"/>
</th>
<td align="left">
<html:password property="password" size="16"/>
</td>
</tr>
<tr>
<td align="right">
<html:submit>
<bean:message key="button.submit"/>
</html:submit>
</td>
<td align="right">
<html:reset>
<bean:message key="button.reset"/>
</html:reset>
</td>
</tr>
</table>
</html:form>
</body>
</html:html>
Следующие пункты проясняют ключевые свойства обработки
форм в Struts, основываясь на приведенном примере:
-
Директива
taglib
говорит компилятору JSP-страницы, где найти дескриптор библиотеки
тэгов (tag library descriptor) для библиотеки тэгов
Struts. В данном случае, мы используем префикс “bean”
(prefix="bean") который идентифицирует тэги из
библиотеки struts-bean, и “html” как префикс, который
идентифицирует тэги из библиотеки struts-html. Вы можете
использовать любой удобный для вас префикс для идентификации
тэгов библиотеки.
-
На этой странице
несколько раз появляется тэг message для того, чтобы извлечь
интернационализованные сообщения из объекта MessageResources,
содержащего все ресурсы данного приложения. Чтобы данная страница
могла работать, следующие ключи сообщений должны быть определены
в ресурсах:
-
logon.title –
заголовок данной страницы входа
-
prompt.username
– строка приглашения для имени пользователя- "Username:"
-
prompt.password
- строка приглашения пароля: "Password:"
-
button.submit
- надпись на кнопке: "Submit"
-
button.reset -
надпись на кнопке:"Reset"
Когда пользователь войдет,
приложение может сохранить объект Locale в сессии пользователя.
Эта Locale будет использоваться для выбора сообщений на
соответствующем языке. Это делает проще реализовать возможность
переключения языков – просто измените сохраненный объект
Locale, и все сообщения переключатся на нужный язык
автоматически.
-
Тэг errors показывает
любые сообщения об ошибках, которые были сохранены компонентами
бизнес-логики, или ничего не показывает, если никаких ошибок не
было записано. Этот тэг будет более подробно описан ниже.
-
Тэг form представляет
HTML элемент <form>, основываясь на заданных атрибутах. Он
также связывает все поля этой формы с объектом FormBean,
хранящемся в сессии под ключем logonForm. Разработчики
Struts-приложения должны обеспечить Java-реализацию этого form
bean, унаследовав его от класса Struts ActionForm. Этот bean
используется для задания начальных значений всем полям ввода,
которые имеют имена, соответствующие именам свойств в этом bean.
Если соответствующий bean не найден, то новый экземпляр будет
создан автоматически, используя указанное имя Java-класса.
(Form beam может быть так же определен в конфигурационном файле
Struts, и в этом случае атрибуты Name и Type могут быть опущены.
Подробнее смотрите Конфигуарционный
фай Action Mappings.)
-
Тэг text представляет
HTML элемент <input> с типом “text”. В этом
случае также указывается число символов (длина), которое будет
иметь данный элемент при отображении в браузере. Когда страница
генерируется, этот элемент получит значение свойства username d
соответствующем bean (то есть, значение, которое будет возвращено
getUsername() ).
-
Тэг password
используется аналогично. Различие в том, что браузер будет
показывать вместо вводимых символов звездочки, когда пользователь
будет набирать свой пароль.
-
Тэги submit и reset
генерируют соответствующие кнопки внизу формы. Текст на кнопках
для каждой кнопки создается с использованием тэга message, также
как и строки приглашений – и таким образом эти значения
интернационализуются.
Обработка multipart-формы также очень проста.
Очевидно, когда вы создаете multipart-форму, вы создаете форму, в
которой есть хоть один элемент типа “file” (<input
type=”file”>). Первым шагом в создании
multipart-формы является использование на JSP-странице библиотеки
тэгов Struts struts-html:
<%@page language="java">
<%@taglib uri="/WEB-INF/struts-html.tld" prefix="html">
<html:form action="uploadAction.do">
Please Input Text:
<html:text property="myText"><br/>
Please Input The File You Wish to Upload:<br/>
<html:file property="myFile"><br />
<html:submit />
</html:form>
Следующим шагом будет создание вашего ActionForm bean:
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.upload.FormFile;
public class UploadForm extends ActionForm {
protected String myText;
protected FormFile myFile;
public void setMyText(String text) {
myText = text;
}
public String getMyText() {
return myText;
}
public void setMyFile(FormFile file) {
myFile = file;
}
public FormFile getMyFile() {
return myFile;
}
}
Взгляните на Javadocs
для FormFile, чтобы ознакомиться с методами, которые он
представляет для манипуляции файлами при их загрузке (file
uploading). Также взгляните на Javadocs для ActionServlet и
ActionMapping, чтобы ознакомиться с различными параметрами,
которые можно задавать, чтобы изменить процесс загрузки файлов.
Обычно в методе perform() в вашем Action-классе вы должны вызвать
((UploadForm) form).getMyFile() , чтобы получить
FormFile и делать с ним все, что пожелаете.
|
|
|
3.3.2
Поддерживаемые типы Input-полей
|
|
Struts определяет HTML-тэги для следующих типов
input-полей (ссылки ведут на соответствующую справочную
информацию):
В любом случае, тэг для поля должен быть вложен в тэг
формы, так, чтобы поле знало, какой bean надо использовать для
инициализации отображаемых значений.
|
|
|
3.3.3
Другие полезные тэги для представления данных
|
|
Есть еще несколько полезных тэгов для создания
представлений. Обращайтесь к документации по каждой конкретной
библиотеке тэгов вместе с изучением Tag Developers Guides.
-
[logic] iterate
повторяет для каждого элемента коллекции (Hashtable, Map. etc)
тело тэга. Это полезно для организации цикла по всем элементам
коллекции.
-
[logic] present
в зависимости от того, какой атрибут установлен, это тэг
проверяет текущий request, и выполняет тело тэга, если в
request'е присутствует это значение. Только один из атрибутов
может использован при каждом использовании тэга, а если вы
используете атрибут-свойство, то в этом случае также требуется
указать имя атрибута. Возможные виды атрибуты включают cookie,
header, name, parameter, property, role, scope, и user.
-
[logic] notPresent
этот тэг-антоним для present. Тэг notPresent обеспечивает ту же
функциональнось, что и present, но только в случае, если
указанный атрибут не присутствует в request. Тэг полезен,
например, для проверки того, зарегистрировался ли пользователь в
системе – и если нет, то перенаправляем управление на
страницу входа.
-
[html] link
генерирует HTML-элемент <a> как anchor definition или как
гиперссылку на указанный URL, и автоматически указывает кодировку
URL чтобы управлять состоянием сессии в случае отсутствии
поддержки cookies.
-
[html] img
генерирует HTML <img> элемент с возможностью динамически
модифицировать URL, определенный атрибутами "src" и
"lowsrc" – подобно тому, как это делает тэг
<html:link>.
-
[bean] parameter
извлекает значение указанного параметра запроса (request
parameter) и определяет результат как атрибут page scope с типом
String или String[].
|
|
|
3.3.4
Автоматическая проверка Form
|
|
Помимо взаимодействия форм и beans, описанного выше,
Struts предлагает дополнительные средства для проверки значений
полей ввода, которые были получены от пользователя. Чтобы
использовать данные средства, переопределите (override) следующий
метод в вашем классе-наследнике ActionForm:
public ActionErrors validate(ActionMapping mapping, HttpServletRequest request);
Метод validate() вызывается сервлетом-контроллером после того, как
свойства bean были заполнены и с помощью submit были посланы на
сервер, но перед тем, как будет вызван соответствующий метод
perform() action-класса. Метод validate() имеет следующие
опции:
The validate() method is called by the controller
servlet after the bean properties have been populated, but before
the corresponding action class's perform() method is
invoked. The validate() method has the following
options:
-
Если выполнили
соответствующие проверки и не нашли никаких проблем – то
возвращаем либо Null, либо экземпляр ActionErrors, имеющий
нулевую длину, и в этом случае сервлет-контроллер вызовет метод
perform() в соответствующем Action-классе.
-
Если выполнили
соответствующие проверки и нашли проблемы – то возвращаем
экземпляр коллекции ActionErrors, содержащий набор экземпляров
ActionError (без -s), где ActionError – это класс, который
содержит ключи сообщений об ошибках (ключи эти расшифрованы,
конечно, в пакете MessageResources приложения ),
которые должны быть показаны. Сервлет-контроллер сохранит этот
массив ошибок в атрибуте request, удобном для использования в
тэге <html:errors>, и затем возвратит управление
обратно ту же форму ввода (которая идентифицируется как свойство
input для данного ActionMapping). В результате получается удобное
средство для обработки ошибочных ситуаций, которое дает
пользователю возможность вернуться в то место, где он ошибся и
исправить ошибку.
Как было указано выше, эта автоматическая проверка
полей ввода является полностью опциональной. Реализация метода
validate() по умолчанию возвращает null, и сервлет-контроллер
предполагает, что вся требуемая проверка будет производится в
action-классе.
Обычный подход – это выполнять первичные,
простейшие проверки с использованием метода ActionForm validate(),
а затем производить проверку с точки зрения бизнес-логики в
action-классе.
Дополнительный пакет для выполнения проверок в
ActionForm доступен в Nightly Build (но не забудьте проверить и
текущий релиз!) и с сайта David
Winterfeldt's Web site.
|
|
|
3.4
Другие методы представления данных
|
|
Хотя стиль вашего приложения (look and feel) может
быть полностью построен на основании стандартных возможностей JSP
и специфических библиотек тэгов Struts, вы можете рассмотреть
возможность использования других методов, которые улучшат
повторное использование компонентов, облегчат эксплуатацию
приложения, и/или уменьшат количество ошибок. Несколько возможных
вариантов рассмотрены ниже в этом разделе.
|
|
|
3.4.1
Тэги для конкретного приложения
|
|
Помимо использования библиотек тэгов, поставляемых
вместе со Struts, легко создавать тэги, которые нужны для
приложения, которое вы строите, чтобы облегчить создание
пользовательского интерфейса. Пример приложения, включенный в
Struts, иллюстрирует принципы создания таких тэгов, на базе
следующих примеров тэгов:
-
checkLogon –
этот тэг проверяет существование конкретного объекта сессии, и
передает управление на страницу logon, если объекта сессии не
существует. Это используется для того, чтобы отловить ситуацию,
когда пользователь добавил страницу из “середины”
вашего приложения в закладки и пытается попасть на эту страницу,
миновав диалог входа, или когда время сессии истекло.
-
linkSubscription
– создает гиперссылку для страницы, содержащей сведения о
Subscription, которое передает требуемый первичный ключ как
атрибут request'а. Это используется для вывода списка подписок
для данного пользователя, и чтобы создать ссылки для их
редактирования или удаления.
-
linkUser –
генерирует гиперссылку на страницу подробностей о пользователе,
передавая требуемый первичный ключ как атрибут запроса.
Исходный код для этих тэгов находится в каталоге
src/example, в package
org.apache.struts.example,
вместе с другими Java-классами, используемыми в данном примере.
|
|
|
3.4.2
Сборка страниц с помощью директивы Includes
|
|
Создание целого представления страницы в одном
JSP-файле (c использованием тэгов и beans для доступа к требуемым
динамическим данным) является очень распространенным подходом к
проектированию, и используется в приложении-примере, включенном в
поставку Struts. Однако, многие приложения требуют отображать
множество логически отличающихся частей вашего приложения вместе
на одной странице.
Например, приложение-портал должно иметь несколько
(или даже все) следующие функциональные возможности на “домашней”
странице портала:
-
Доступ к поисковому
механизму данного портала.
-
Одну или более
“новостных лент”, с разделами по интересам согласно
пользовательскому профилю.
-
Доступ к форумам
этого, связанных с разделами.
-
“Индикатор
ожидания почты”, если ваш портал поддерживает бесплатные
email.
Разработка различных сегментов сайта становится проще,
если вы разделите свою работу, и назначите различных разработчиков
на различные сегменты. Затем можно будет использовать возможность
include
в технологии
JavaServer Pages для того чтобы объединить результаты в единую
результирующую страницу, или использовать тэг include,
поставляемый вместе со Struts. Есть три возможных типа include,
зависящих от того, когда нужно получить комбинацию результатов:
-
Директива <%@
include file="xxxxx" %> может подключить
файл, который содержит Java-код или JSP-тэги. Код во включаемом
файле может даже ссылаться на переменные, объявленные выше во
внешней JSP-странице. Код встраивается (inlined) в другую
JavaServer Page до того, как он компилируется, и таким образом он
определенно содержать нечто большее, чем просто HTML.
-
include action
(<jsp:include page="xxxxx" flush="true"
/> ) обрабатывается во время запроса, и управляется
прозрачно для сервера. Помимо всего прочего, это означает, что вы
можете выполнять условную сборку страницы из подключений, если
вложите их в тэг вроде equals.
-
Тэг bean:include
принимает в качестве аргумента либо “forward”,
представляющий логическое имя, связанное с подключаемой jsp, либо
аргумент “id”, который представляет String-переменную
context, используемую для вывода jsp-страницы.
Другим подходом может быть использование библиотеки
тэгов шаблонов Struts (Struts Template Tag library). Подробнее
смотрите Руководство Разработчика.
Использование Tiles является альтернативой
первоначальной библиотеки Template Tag. Tiles предлагают несколько
расширений и новых возможностей. Tiles доступны в Nightly Build
(но не забудьте проверить и текущий релиз!) и с сайта Cedric
Dumoulin's Web site.
|
|
|
3.4.3
Компоненты обработки изображений
|
|
Некоторые приложения требуют наличия динамически
создаваемых изображений, вроде графиков цен. Два различных подхода
обычно используются для удовлетворения этих требований:
-
Представить
гиперссылку с URL, который выполнит запрос сервлета. Сервлет
будет использовать графическую библиотеку чтобы представить
графическое изображение, соответственно установить тип
содержимого (content type) - такое, как image/gif, и
возвратить сгенерированные байты обратно браузеру, который
покажет точно так же, как если бы он получил статический файл.
-
Представить HTML-код, необходимый для загрузки
Java-апплета, который создаст требуемый график. Вы можете
сконфигурировать график, устанавливая соответствующие параметры
инициализации для апплета в представленном коде, или вы можете
иметь апплет, который устанавливает свое собственное соединение с
сервером для получения этих параметров
|
|
|
3.4.4
Представление текста
|
|
Некоторые приложения требуют использование динамически
сгенерированного текста или разметки, например XML. Если целая
страница представляется таким образом, и есть возможность
использовать PrintWriter для получения вывода, то очень просто
сделать это из Action:
response.setContentType("text/plain"); // or text/xml
PrintWriter writer = response.getWriter();
// use writer to render text
return(null);
Далееt: Разработка
Controller-
компонентов
|
|
|
Copyright
(c) 2000-2002, Apache Software Foundation
|