10.2.2 常用的用户输入验证实现方式
10.2.1中虽然实现了用户登陆功能的,用户输入验证。但是这种方式却很少被使用。本节中介绍实际应用中较为常见的编码方式。代码实现进需要修改 Action类,其余文件的内容无需修改。
代码10-1中登陆案例代码编写中将用户输入验证的代码实现放到execute方法中,这种方法虽然容易被读者理解。但是从代码设计角度来看存在execute方法过于臃肿、不利于代码的重用等问题。更为常见的代码实现方式,则是重写ActionSupport类中的validate方法,在该方法中完成用户输入验证。具体代码如下所示:
packagecom.study.erp.action;
importcom.opensymphony.xwork2.ActionSupport;
public class LoginAction extendsActionSupport{
private String username;
private String password;
// 此处省略属性对应的 get 和 set 方法
….
@Override
public void validate() {
if(username == null || username.trim().equals("")){
addFieldError("username", "用户名不能为空.");
}
if(password == null || password.trim().equals("")){
addFieldError("password", "密码不能为空.");
}else if(password.length()<6 || password.length()>12){
addFieldError("password", "密码长度应在6到12位之间.");
}
}
public String execute(){
return "success";
}
}
代码10-4用户登陆功能LoginAction
比较上述10-4代码和10-1代码读者可以发现,10-4的内容更加简洁。当进行用户输入验证时,该验证代码编写到validate方法。该方法中如果发现用户输入错误时,将错误信息添加到框架FieldError中即可。在进行完所有的用户输入验证后,无需用户编码实现页面的跳转。框架会验证用户输入验证是否正确,如果正确会继续执行execute方法,如果错误会跳转到该Action对应的名为input的结果视图中。
本节案例中的代码实现,是Struts框架下基于编码方式实现用户输入验证所提倡的方式。读者在编写时常见的错误是没有在配置文件中配置名为input的结果视图,其配置文件的代码为:<result name="input">/xxx.jsp</result>。
10.2.3 多方法Action的用户输入验证实现方式
在学习第二章 Action的编写和配置时,我们已经了解到 Struts2的 Action类中可以包含多个方法,每个方法用来完成不同的逻辑操作。例如,我们可以利用同一个 Action来完成一组逻辑相关的操作:用户登陆、用户注册、用户信息修改和用户信息删除。代码实现时需要在一个 Action类中需要包括 4个方法,但是对于这 4个方法来说用户输入校验的规则也是不同的,如果再使用 10.2.2所讲的 validate方法来实现用户输入验证,很难实现预期的效果。
Struts2框架对于这类情况也提供了相应的解决方案。当 Action中包括多个逻辑操作方法时,为完成用户输入验证 Action类的编码须实现 Validateable接口( ActionSupport类已经实现了 Validateable接口)并编写 validateXxx方法, Xxx即对应 Action逻辑操作方法的名字。 Struts2框架利用了反射机制在对应的逻辑操作方法执行之前调用相应的 validateXxx方法。
为了让读者更容易读懂本节中的代码,本案例中仅实现了用户注册和用户登陆功能的用户输入校验。用户登陆的用户输入验证规则不变,用户注册功能的用户输入验证规则包括:用户名不能为空、密码不能为空、密码长度必须在6到12位之间、年龄的输入范围必须在0-100之间,邮箱必须包括@字符。
用户登陆功能图示与图10-1,10-2,10-3一致,以下为用户注册功能截图:
(1) 当在页面上只输入年龄-11,邮箱为11时
图10-3 用户注册功验证错误1
(2) 当在页面上输入用户名为11,密码为11,年龄为-11,邮箱为11时:
图10-4 用户注册功验证错误2
(3) 当在页面上输入用户名为唐琳,密码为 123456,年龄为 33,邮箱为 tanglin@dlut.edu.cn时:
图10-5 用户注册功验证成功
以下为【用户登陆功能】、【用户注册功能】实现的代码结构:
图10-6 用户登陆、用户注册功验证代码结构
编写该功能时用户需要编写和创建的文件:
文件名 | 说明 | 备注 |
Login.jsp | 用户登陆页面 | 视图 |
LoginSuccess.jsp | 用户登陆成功页面 | 视图 |
Regist.jsp | 用户注册页面 | 视图 |
RegistSuccess.jsp | 用户注册成功页面 | 视图 |
UserAction | Action类 | 控制器 |
struts.xml | Struts2框架的配置文件 | 配置文件 |
web.xml | 项目的部署描述文件 | 配置文件 |
由于篇幅的关系,以下仅列出UserAction类,Regist.jsp和struts.xml文件的部分代码。其它代码参考光盘中Unit10文件夹下03的项目代码。
UserAction类包含两个逻辑,因此包含了两个逻辑方法分别是 login和 regist。 login方法用于实现用户登陆功能,对应的用户输入校验方法为 validateLogin方法; regist方法用于实现用户注册功能,对应的用户输入校验方法为 validateRegist方法。具体代码如下所示:
packagecom.study.erp.action;
importcom.opensymphony.xwork2.ActionSupport;
public class UserActionextends ActionSupport{
private String username;
private String password;
private int age;
private String email;
// 此处省略属性对应的 get 和 set 方法
….
// 用户登陆功能对应的用户输入校验方法
public void validateLogin() {
if(username == null || username.trim().equals("")){
addFieldError("username"," 用户名不能为空 .");
}
if(password == null || password.trim().equals("")){
addFieldError("password", " 密码不能为空 .");
}else if(password.length()<6 || password.length()>12){
addFieldError("password", " 密码长度应在 6 到 12 位之间 .");
}
}
// 用户注册功能对应的用户输入校验方法
public void validateRegist() {
if(username == null || username.trim().equals("")){
addFieldError("username", " 用户名不能为空 .");
}
if(password == null || password.trim().equals("")){
addFieldError("password", " 密码不能为空 .");
}else if(password.length()<6 || password.length()>12){
addFieldError("password", " 密码长度应在 6 到 12 位之间 .");
}
if(age <0 || age>100){
addFieldError("age", " 年龄不符合要求 .");
}
if(email.indexOf("@") == -1){
addFieldError("email", "Email 格式不符合要求 .");
}
}
public String login(){
return "success";
}
public String regist(){
return "success";
}
}
以上代码中用户输入校验方法名编写时务必注意 : 1.为 validateXxx方法 ,方法名 Xxx为对应的逻辑方法方法名,首字母大写。 2. 方法的权限为 public,方法没有参数,返回值为 void类型。
struts.xml文件中当前的 Action类对应的每个 action配置都需要包含名为“ input”的结果视图,具体的配置信息如下所示:
<?xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTDStruts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name= "default"namespace= "/" extends= "struts-default">
<action name="login"class="com.study.erp.action.UserAction" method="login">
<result name="input">/Login.jsp</result>
<result name="success">/LoginSuccess.jsp</result>
</action>
<action name="regist"class="com.study.erp.action.UserAction" method="regist">
<result name="input">/Regist.jsp</result>
<result name="success">/RegistSuccess.jsp</result>
</action>
</package>
</struts>
用户注册视图 Regist.jsp 代码如下所示 :
<%@ pagelanguage="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%@ tagliburi="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title> 用户注册页面 </title>
</head>
<body>
<h1> 用户注册 </h1>
<hr>
<s:form action="regist.action">
<s:textfield name="username" label="用户名"></s:textfield>
<s:password name="password" label="密码"></s:password>
<s:textfield name="age" label="年龄"></s:textfield>
<s:textfield name="email" label="电子邮件:"></s:textfield>
<s:submit name="login" value="注册"/>
</s:form>
</body>
</html>
以上为实现登陆和注册功能的主要代码,我们仔细分析发现 validateLogin() 和 validateRegist() 代码中部分代码是重复的,但是上面的代码中由于用户输入验证对应的方法是不同的,所以代码出现了重复的现象,虽然我们可以单独把这段重复的代码单独写到一个方法中,然后在这两个用户输入验证方法中进行调用以进行改进。
Struts2 框架的 validate 方法是由 DefaultWorkFlowInterceptor 拦截器调用的。该拦截器包含在 defaultStack 拦截器栈中。因此,只要使用默认的拦截器栈该拦截器都回被运行,从而 validate 方法会被调用。换句话说,默认情况下当前 Action 的所有逻辑方法执行之前都会执行 validate 方法。如果 validate 方法和 validateXxx 方法同时存在,它们的执行顺序是先执行 validateXxx 方法,再执行 validate 方法。针对于 Struts 框架中本例中的 Action 类可以做如下优化:
packagecom.study.erp.action;
importcom.opensymphony.xwork2.ActionSupport;
public class UserActionextends ActionSupport{
private String username;
private String password;
private int age;
private String email;
//此处省略属性对应的 get和 set方法
….
//用户登陆功能对应的用户输入校验方法,此时本方法可以省略不写
public void validateLogin() {
}
//用户注册功能对应的用户输入校验方法
public void validateRegist() {
if(age <0 || age>100){
addFieldError("age", "年龄不符合要求.");
}
if(email.indexOf("@") == -1){
addFieldError("email", "Email格式不符合要求.");
}
}
//用户输入验证方法
public void validate(){
if(username == null || username.trim().equals("")){
addFieldError("username", "用户名不能为空.");
}
if(password == null || password.trim().equals("")){
addFieldError("password", "密码不能为空.");
}else if(password.length()<6 || password.length()>12){
addFieldError("password", "密码长度应在6到12位之间.");
}
}
public String login(){
return "success";
}
public String regist(){
return "success";
}
}
如果某 action 中包含了多个逻辑方法,其中绝大部分方法都有相同的验证规则,只有个别方法不需要该验证规则时。可以使用 DefaultWorkFlowInterceptor 拦截器的 excludeMethods 参数,指定被排除的方法。如果被排除的方法超过 1 个,方法名中间使用逗号进行间隔。
例如 UserAction 类中包含了 3 个方法:用户登陆 login 方法,用户注册 regist 方法,用户信息删除的 delete 方法。而用户信息删除不需要执行 validate 方法用户名和用户密码的验证规则。具体的实现方法可以包括以下两种,比较而言第二种方法更容易理解也更加简单。
1 .重新设定默认的拦截器栈,在该拦截器栈中将 delete 方法排除掉。对应的 struts.xml 文件的内容如下所示:
<struts>
<package name= "default"namespace= "/" extends= "struts-default">
<interceptors>
<interceptor-stackname="mydefaultStack">
<interceptor-refname="defaultStack">
<paramname="workflow.excludeMethods">delete</param>
</interceptor-ref>
</interceptor-stack>
</interceptors>
<default-interceptor-refname="mydefaultStack"></default-interceptor-ref>
<action name= "login"class= "com.study.erp.action.UserAction" method= "login">
<result name= "input">/Login.jsp</result>
<result name= "success">/LoginSuccess.jsp</result>
</action>
<action name= "regist"class= "com.study.erp.action.UserAction" method= "regist">
<result name= "input">/Regist.jsp</result>
<result name= "success">/RegistSuccess.jsp</result>
</action>
<action name= "delete"class= "com.study.erp.action.UserAction" method= "delete">
<result name= "success">/DeleteSuccess.jsp</result>
</action>
</package>
</struts>
2 .只在删除用户信息对应的 action 配置信息中将 delete 方法从拦截器中删除掉。对应的配置文件内容
<?xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTDStruts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name= "default"namespace= "/" extends= "struts-default">
<action name= "login"class= "com.study.erp.action.UserAction" method= "login">
<result name= "input">/Login.jsp</result>
<result name= "success">/LoginSuccess.jsp</result>
</action>
<action name= "regist"class= "com.study.erp.action.UserAction" method= "regist">
<result name= "input">/Regist.jsp</result>
<result name= "success">/RegistSuccess.jsp</result>
</action>
<action name= "delete"class= "com.study.erp.action.UserAction" method= "delete">
<result name= "success">/DeleteSuccess.jsp</result>
<interceptor-ref name="defaultStack">
<paramname="workflow.excludeMethods">delete</param>
</interceptor-ref>
</action>
</package>
</struts>