Занятие 1. Основные понятия. Авторизация пользователя
Мы начинаем публикацию цикла учебных статей по Java-фреймворку Struts 2.
Базовым во фреймворке Struts является понятие action (экшн, действие). На то, какой экшн будет выполняться, указывает URL страницы. Наиболее общая схема работы такова. Struts получает на управление экшн и обрабатывает его: указание на экшн находится в конфигурационном файле struts.xml, в зависимости от имени экшна инициализируется метод указанного в параметрах класса, а уже в зависимости от того, что именно метод возвращает, инициализируется результат, в качестве значения которого указан конкретный файл, который и загружается.
Поясним на конкретном примере (см. иллюстрацию).
Мы набираем в адресной строке адрес www.mysite.com (к которому браузер автоматически добавляет имя протокола по умолчанию, то есть http://www.mysite.com/), и в этом адресе нет указания ни на какой экшн. Но при загрузке сервера Apache Tomcat читает конфигурационный файл web.xml, в котором есть группа тэгов <welcome-file-list>, отвечающая за загрузку файла по умолчанию: <welcome-file>index.jsp</welcome-file> (если не указан адрес конкретной страницы). В файле index.jsp находится строка
<% response.sendRedirect("login.action"); %>
которая выполняет редирект (перенаправление) на определённую страницу, идентифицированную как login.action — а в этом URL уже есть указание на экшн. Это производит то же самое действие, как если бы мы написали в адресной строке www.mysite.com/login.action.
Получив на обработку экшн, Struts ищет его обработчик в конфигурационном файле struts.xml:
<action name="login" class="mymovieteach.action.LoginAction"
method="input">
<result type="dispatcher" name="success">
/WEB-INF/jsp/login.jsp
</result>
</action>
Среди однотипных тэгов <action> Struts анализирует именно этот, потому что параметр name этого тэга содержит значение «login». Остальные два параметра указывают на класс и метод, с помощью которых будет производиться обработка экшна. Значение параметра class — в примере это mymovieteach.action.LoginAction — является полным именем класса, где mymovieteach.action — название пакета, а LoginAction — название класса. Struts создаёт экземпляр данного класса, заполняет его значениями через «сеттеры». Это название заимствовано из технологии JavaBeans — классов для задания значений свойствам объектов).
Примечание. JavaBeans — классы в языке Java, написанные по определённым правилам. Они используются для объединения нескольких объектов в один bean для удобной передачи данных. Одним из таких правил является то, что свойства класса должны быть доступны только через get, set или is (для полей логического типа), которые подчинятся стандартному соглашению об именах.
После этого Struts выполняет валидацию, если это нужно (про валидацию см. ниже). В созданном классе вызывается метод input() (как следует из method="input") — для этого метода валидация не требуется. Результат вызова данного метода определяет дальнейшее развитие событий. В даном примере метод возвращает значение SUCCESS:
public String input() throws Exception {
return SUCCESS;
}
По этому значению управление передаётся результату:
<result type="dispatcher" name="success">
/WEB-INF/jsp/login.jsp</result>
Имя (name) результата — success, то есть то значение, которое вернул метод. Эта пара «параметр-значение» является парой по умолчанию, и её можно не указывать. То есть тэг можно было записать так:
<result type="dispatcher">/WEB-INF/jsp/login.jsp</result>
Тип результата ("dispatcher") обозначает, что нужно отобразить файл, указанный в тэге. В данном случае содержимым тэга является адрес файла: /WEB-INF/jsp/login.jsp.
Формирование файлов страниц в пределах фреймворка Struts подчиняется особой логике. Если мы включаем в файл обработку программной логики, то в первых строчках нужно сделать указания на библиотеки, обрабатывающие тэги, которые будут заменяться на HTML-вывод или вызывать программную логику.
Рассмотрим конкретный пример — файл login.jsp.
<%@ taglib prefix="s" uri="/struts-tags" %> <html> <head></head> <body> <div> <s:fielderror><s:param>invalidLoginPassw</s:param></s:fielderror> <s:fielderror><s:param>name</s:param></s:fielderror> <s:fielderror><s:param>password</s:param></s:fielderror> </div> <s:form name="loginForm" action="loginSubmit" method="post" > <table> <tr> <td>Name:</td> <td><s:textfield name="name"/></td> </tr> <tr> <td>Password:</td> <td><s:password name="password"/></td> </tr> <tr> <td></td> <td><s:submit value="Login"/></td> </tr> </table> </s:form> </body> </html>
Первая строчка — <%@ taglib prefix="s" uri="/struts-tags" %> — является включением библиотеки тэгов struts-tags. Включение сделано с сохранением синтаксиса JSP. В качестве параметра prefix мы используем значение «s» — это значит, что тэги будут иметь вид <s:тэг></s:тэг> или (если тэг не парный) <s:тэг/>.
Далее идёт обычный вывод HTML, но внутри него встречаются собственно Struts-тэги. Например, <s:textfield name="name"/> будет при анализе файла заменён на элемент формы — текстовое поле <input type="text" name="name"> — с атрибутом name, равным значению «name» (нетрудно догадаться, что если бы мы написали <s:textfield name="abc"/>, то значение атрибута name в результирующем HTML-выводе было бы равным «abc»).
Подобным образом формируется сама HTML-форма, поля для ввода логина и пароля, кнопка Submit, а также сообщения об ошибках. Если с текстовыми полями и кнопкой вопросов возникнуть не должно, то с формированием тэга формы дело обстоит чуть сложнее. Тэг
<s:form name="loginForm" action="loginSubmit" method="post" > ... </s:form>
содержит атрибуты, которые будут использованы для генерации атрибутов HTML-формы. Атрибуты method="post" и name="loginForm" с их значением останутся неизменными. Атрибут action содержит в качестве значения имя экшна, но при формировании HTML-кода это имя заменится на конкретный URL, то есть loginSubmit.action (расширение .action используется в Struts 2 по умолчанию — в Struts 1 использовалось расширение .do), на который с помощью метода POST будут посланы данные при нажатии кнопки Submit.
Итак, страница сформирована, обработана с помощью Struts. Пользователь заполняет (или не заполняет) поля логина и пароля, нажимает на кнопку Submit (или на Enter на клавиатуре, что аналогично вызывает событие Submit), тем самым пересылая данные на экшн loginSubmit.
Обращаемся к struts.xml и ищем обработку этого экшна:
<action name="loginSubmit" class="mymovieteach.action.LoginAction"
method="loginSubmit">
<result type="dispatcher" name="input">
/WEB-INF/jsp/login.jsp</result>
<result type="redirect">home.action</result>
</action>
В деталях процесс идёт так: с помощью метода POST данные передаются серверу, который передаёт их на управление в Struts классу LoginAction. Struts создаёт экземпляр данного класса, заполняет его значениями через «сеттеры». Значения обрабатываются классом. Поскольку этот класс наследует валидацию (подробнее про валидацию будет рассказано в следующем занятии) — public class LoginAction extends ActionSupport implements Validateable, для любого метода, кроме input() (который нужен только для вывода формы) запускается валидация.
public void validate() {
if (this.getName().length() < 1 ||
this.getPassword().length() < 1) {
if (this.getName().length() < 1)
this.addFieldError("name", "Name is required");
if (this.getPassword().length() < 1)
this.addFieldError("password", "Password is required");
} else {
if (!"user".equalsIgnoreCase(this.getName()) ||
!"user".equalsIgnoreCase(this.getPassword()))
this.addFieldError("invalidLoginPassw",
"Invalid login/password");
}
}
В зависимости от результата производятся дальнейшие действия, описанные в struts.xml. Если заполнение формы не прошло валидацию, выбирается результат с name="input" (в нашем случае результат такой: <result type="dispatcher" name="input">/WEB-INF/jsp/login.jsp</result>) — соответственно, опять грузится файл login.jsp, но уже с инициализированными сообщениями об ошибках.
В процессе валидации формируются одно или несколько сообщений об ошибках (this.addFieldError()) — «name», «password» или «invalidLoginPassw». Например,
<s:fielderror><s:param>name</s:param></s:fielderror>
вызывается, когда поле name в форме не заполнено (if (this.getName().length() < 1)), заменяется на HTML-вывод с сообщением “Name is required”.
Если же валидация прошла успешно, то Struts передаёт управление методу loginSubmit класса LoginAction, и если метод вернул вернул значение SUCCESS, то берётся результат с name по умолчанию, то есть строка <result type="redirect">home.action</result>.
В последнем случае инициализирован экшн home — ищем его в struts.xml:
<action name="home" class="mymovieteach.action.HomeAction"
method="show">
<result type="dispatcher">
/WEB-INF/jsp/home.jsp</result>
</action>
Экшн вызывает метод show() класса по адресу mymovieteach.action.HomeAction; метод возвращает SUCCESS, и как следствие, загружается результат по умолчанию, то есть содержимое тэга <result type="dispatcher">/WEB-INF/jsp/home.jsp</result> — файл home.jsp. Вот класс HomeAction:
package mymovieteach.action;
import com.opensymphony.xwork2.ActionSupport;
public class HomeAction extends ActionSupport {
public String show() throws Exception {
return SUCCESS;
}
}
Содержимое файла home.jsp:
<html> <head></head> <body> Welcome! </body> </html>
Таким образом, сообщение «Welcome!» на странице в браузере мы получим, если валидация прошла успешно, и пользователь вошёл на сайт. В адресной строке при этом будет адрес http://www.mysite.com/home.action.
Приложение
Полный листинг класса LoginAction:
package mymovieteach.action;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.Validateable;
public class LoginAction extends ActionSupport
implements Validateable {
private String name;
private String password;
public String getName() {
return name == null ? "" : name.trim();
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password == null ? "" : password.trim();
}
public void setPassword(String password) {
this.password = password;
}
public String input() throws Exception {
return SUCCESS;
}
public void validate() {
if (this.getName().length() < 1 ||
this.getPassword().length() < 1) {
if (this.getName().length() < 1)
this.addFieldError("name",
"Name is required");
if (this.getPassword().length() < 1)
this.addFieldError("password",
"Password is required");
} else {
if (!"user".equalsIgnoreCase(this.getName()) ||
!"user".equalsIgnoreCase(this.getPassword()))
this.addFieldError("invalidLoginPassw",
"Invalid login/password");
}
}
public String loginSubmit() throws Exception {
return SUCCESS;
}
}
Кратко повторим основной путь, который проходит формирование страницы от запроса пользователя до того, как окончательный вариант страницы будет загружен в браузер пользователя.
1. Пользователь набирает адрес в адресной строке браузера или нажимает на ссылку. Или, заполнив поля формы, нажимает кнопку Submit.
2. В первом случае тут может быть промежуточный этап: редирект. Допустим, в набранном адресе вообще нет указания на экшн, который необходим для обработки. В этом случае файл web.xml (загруженный в память ещё при старте системы) перехватывает запрос, указывает файл, который должен быть загружен, а в этом файле существует указание на экшн.
3. С помощью методов GET (в случае с набором адреса или переходом по ссылке) или POST (в случае с отправкой данных формы) данные посылаются на сервер.
4. Сервер (в нашем случае Apache Tomcat) принимает данные и передаёт их на управление фреймворку Struts.
5. Фреймворк получает в качестве одного из переданных значений имя экшна, который должен быть выполнен. В составе приложения есть конфигурационный файл struts.xml, в котором перечислены все допустимые экшны и связанные с ними классы, методы и результаты выполнения методов. В соответствии с именем экшна выбирается класс и указывается метод.
6. Класс заполняется значениями, если они переданы через форму. Для этого используются т.наз. «сеттеры».
7. До того, как будет выполнен указанный метод, запускается валидация переданных данных (если требуется). Если она пройдена успешно,
8. выполняется метод, указанный для экшна, и если возвращено значение SUCCESS, выполняется результат по умолчанию. Если же нет, класс возвращает значение, по которому экшн выбирает иной результат (например, возврат к форме с указанием на ошибки).
9. Значение результата выполнения метода — адрес файла. Результат обработки этого файла и загружается в браузер пользователя.
Второй и седьмой пункты, как видно, работают не всегда.


Copyright © 19992012 LAR