当我们使用Struts2表单时,它是一种增强型的表单,自带了错误信息输出的功能,不需要指定它的fielderror等其他的信息,并且其错误信息出现在对应输入框的上面。
如下实例:
首先,login2.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%@ taglib uri="/struts-tags" prefix="s" %><%--pay attention to uri --%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'login2.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<s:form action="login.action" method="Get">
<s:textfield name="username" label="username"></s:textfield>r<br/>
<s:password name="password" label="password"></s:password>
<s:submit value="submit"></s:submit>
</s:form>
</body>
</html>
然后是LoginAction.java:
package com.test.action;
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport
{
private String username;
private String password;
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
public String execute() throws Exception
{
return "success";
}
@Override
public void validate()
{
if(null == username || -1 != username.indexOf(("hello")))
{
this.addFieldError("username", "username invalid");
}
if(null == password || password.length() < 4)
{
this.addFieldError("password", "password invalid");
}
}
}
struts.xml文件相关部分配置如下:
<action name="login" class="com.test.action.LoginAction">
<result name="success">/result.jsp</result>
<result name="input">/login2.jsp</result>
</action>
一个输出样例:
下面给出利用校验框架进行校验的实例:
register.jsp如下:
<%@ page language="java" import="java.util.*" pageEncoding="GBK"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'register.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<table align="center" width="40%" border="0">
<tr>
<td>
<s:actionerror cssStyle="color:red"/><%--actionerror中增加的所有信息显示到页面上 --%>
__________________________________________________
<s:fielderror cssStyle="color:blue"></s:fielderror>
</td>
</tr>
</table>
<form action="register.action" method="post">
<table align="center" width="40%" border=1">
<tr>
<td>
username:
</td>
<td>
<input type="text" name="username" size="20">
</td>
</tr>
<tr>
<td>
password:
</td>
<td>
<input type="password" name="password" size="20">
</td>
</tr>
<tr>
<td>
re-password:
</td>
<td>
<input type="password" name="repassword" size="20">
</td>
</tr>
<tr>
<td>
age:
</td>
<td>
<input type="text" name="age" size="20">
</td>
</tr>
<tr>
<td>
birthday:
</td>
<td>
<input type="text" name="birthday" size="20">
</td>
</tr>
<tr>
<td>
graduation:
</td>
<td>
<input type="text" name="graduation" size="20">
</td>
</tr>
<tr>
<td>
<input type="submit" value="submit"/>
</td>
<td>
<input type="reset" value="reset"/>
</td>
</tr>
</table>
</form>
</body>
</html>
RegisterAction.java如下:
package com.test.action;
import java.util.Calendar;
import java.util.Date;
import com.opensymphony.xwork2.ActionSupport;
public class RegisterAction extends ActionSupport
{
private String username;
private String password;
private String repassword;
private int age;
private Date birthday;
private Date graduation;
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
public String getRepassword()
{
return repassword;
}
public void setRepassword(String repassword)
{
this.repassword = repassword;
}
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
}
public Date getBirthday()
{
return birthday;
}
public void setBirthday(Date birthday)
{
this.birthday = birthday;
}
public Date getGraduation()
{
return graduation;
}
public void setGraduation(Date graduation)
{
this.graduation = graduation;
}
@Override
public String execute() throws Exception
{
System.out.println("execute invoked");
return SUCCESS;
}
}
success.jsp如下:
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'success.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<table align="center" width="40%" border=1">
<tr>
<td>
username:
</td>
<td>
${requestScope.username }
</td>
</tr>
<tr>
<td>
password:
</td>
<td>
${requestScope.password }
</td>
</tr>
<tr>
<td>
age:
</td>
<td>
${requestScope.age }
</td>
</tr>
<tr>
<td>
birthday:
</td>
<td>
${requestScope.birthday }
</td>
</tr>
<tr>
<td>
graduation:
</td>
<td>
${requestScope.graduation }
</td>
</tr>
</table>
</body>
</html>
struts.xml如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="struts2" extends="struts-default">
<action name="helloworld" class="com.test.action.HelloWorld">
<result name="success">/helloworld.jsp</result>
</action>
<action name="login" class="com.test.action.LoginAction">
<result name="success">/result.jsp</result>
<result name="input">/login2.jsp</result>
</action>
<action name="converterAction" class="com.test.action.PointAction" method="test">
<result name="success">/output.jsp</result>
</action>
<action name="register" class="com.test.action.RegisterAction">
<result name="success">/success.jsp</result>
<result name="input">/register.jsp</result>
</action>
</package>
</struts>
下面就是重头戏了,在和RegisterAction同包目录下建立RegisterAction-validation.xml文件,文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator 1.0.2//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
<validators>
<field name="username">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>username invalid</message>
</field-validator>
<field-validator type="stringlength">
<param name="minLength">6</param>
<param name="maxLength">10</param>
<message>useranme should be between ${minLength} and ${maxLength}</message>
</field-validator>
</field>
<field name="password">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>password invalid</message>
</field-validator>
<field-validator type="stringlength">
<param name="minLength">6</param>
<param name="maxLength">10</param>
<message>password should be between ${minLength} and ${maxLength}</message>
</field-validator>
</field>
<field name="repassword">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>repassword invalid</message>
</field-validator>
<field-validator type="stringlength">
<param name="minLength">6</param>
<param name="maxLength">10</param>
<message>repassword should be between ${minLength} and ${maxLength}</message>
</field-validator>
</field>
<field name="age">
<field-validator type="required">
<message>age invalid</message>
</field-validator>
<field-validator type="int">
<param name="min">1</param>
<param name="max">150</param>
<message>age should be between ${min} and ${max}</message>
</field-validator>
</field>
<field name="birthday">
<field-validator type="required">
<message>birthday invalid</message>
</field-validator>
<field-validator type="date">
<param name="min">2000-01-10</param>
<param name="max">2003-11-11</param>
<message>birthday should be between ${min} and ${max}</message>
</field-validator>
</field>
<field name="graduation">
<field-validator type="required">
<message>graduation invalid</message>
</field-validator>
<field-validator type="int">
<param name="min">2005-01-10</param>
<param name="max">2008-11-11</param>
<message>graduation should be between ${min} and ${max}</message>
</field-validator>
</field>
</validators>
注意的一点是这里的field-validator的type有"requiredstring"、"stringlength",param的name有minLength、trim、max等,我们是如何知道这些的呢?答案是:找到xwork-2.1.2.jar下的包com.opensymphony.xwork2.validator.validators,里面有一个配置文件叫做default.xml,打开看看你就明白了:
根据这里的name就是我们校验的type,在该包下面找到对应的处理类,查看其get和set方法,我们就可以获悉param中的name的取值有哪些(getX,setX的后缀部分,因为这里利用的是反射的机制进行处理,因此处理类的属性可以和name的取值不同,但是方法名的后缀一定要相同)。
此外,还有一种方式是校验器优先,他不同与我们上面的属性优先的方式:
<!--校验器优先校验规则 -->
<validator type="requiredstring">
<param name="fieldName">username</param>
<message>username invalid</message>
</validator>
<validator type="stringlength">
<param name="fieldName">username</param>
<param name="minLength">6</param>
<param name="maxLength">10</param>
<message>username should be between ${minLength} and ${maxLength}</message>
</validator>
两者采用哪种方式均可,没有孰优孰劣之分。
还有一种无所谓优劣之分的Action的实现方式:我们上面的使用的一直是一种叫做属性驱动Action的东东,其实还有一种模型驱动Action,他在属性值比较多的时候很有用,比如我们可以写一个RegisterAction2.java来代替RegisterAction.java(需要实现ModelDriven<T>这个泛型接口,并且实现其中的方法getModel()):
package com.test.action;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.test.bean.User;
public class RegisterAction2 extends ActionSupport implements ModelDriven<User>
{
private User user = new User();
public User getModel()
{
return user;
}
@Override
public String execute() throws Exception
{
return SUCCESS;
}
}
然后,在com.test.bean中建立User.java:
package com.test.bean;
import java.util.Date;
public class User
{
private String username;
private String password;
private String repassword;
private int age;
private Date birthday;
private Date graduation;
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
public String getRepassword()
{
return repassword;
}
public void setRepassword(String repassword)
{
this.repassword = repassword;
}
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
}
public Date getBirthday()
{
return birthday;
}
public void setBirthday(Date birthday)
{
this.birthday = birthday;
}
public Date getGraduation()
{
return graduation;
}
public void setGraduation(Date graduation)
{
this.graduation = graduation;
}
}
多提一点:为了防止并发带来的问题,与Servlet不同的是Action对象是多实例的,一个请求对应一个Action对象。
还有一个问题,就是基于用户界面友好性的要求,用户输入的信息应该保存起来,即让用户知道哪里错了,又让用户能在原有的基础上进行修改,如何做到这一点呢?可以利用struts2标签库:不仅内置了错误信息输出功能,而且有一个保留输入的提交信息这样的功能(当然处于安全性的考虑,密码没有保留)。因此对register.jsp做出如下修改即可:
<body>
<s:form action="register">
<s:textfield name="username" label="username"></s:textfield>
<s:password name="password" label="password"></s:password>
<s:password name="repassword" label="repassword"></s:password>
<s:textfield name="age" label="age"></s:textfield>
<s:textfield name="birthday" label="birthday"></s:textfield>
<s:textfield name="graduation" label="graduation"></s:textfield>
<s:submit value="submit"></s:submit>
</s:form>
</body>
当然,前提是要引入标签库:<%@ taglib uri="/struts-tags" prefix="s" %>
最后,如果即使用编码的方式进行校验,同时又用校验框架进行校验,结果会如何?校验流程如下:
- 首先执行类型转换
- 执行对应的校验框架(XML)
- 执行特定方法对应的validate验证(test,validateTest)
- 执行validate()方法
如果在以上所有过程当中,发现了任何错误,都不再去执行execute方法或指定的特定方法(test),页面转向了struts.xml中input这个result所对应的页面。