当我们使用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" %>
最后,如果即使用编码的方式进行校验,同时又用校验框架进行校验,结果会如何?校验流程如下:
如果在以上所有过程当中,发现了任何错误,都不再去执行execute方法或指定的特定方法(test),页面转向了struts.xml中input这个result所对应的页面。