Struts2利用验证框架实现数据验证(十一)

勿以恶小而为之,勿以善小而不为--------------------------刘备

劝诸君,多行善事积福报,莫作恶

上一章简单介绍了Struts2实现单文件上传,多文件上传与下载(十),如果没有看过,请观看上一章

数据验证,分为前端验证和后端验证。

前端验证主要是通过JavaScript和jQuery来实现,目的是: 减少客户等待的时间,减轻服务器端的压力,用户输入的错误数据,直接提示。

后端验证的目的是: 防止“绕过”客户端,提交非法数据。 在服务器端处理数据之前,确认数据的正确性。

这里只讲后端的验证。

在验证的时候,需要将提示信息展示给前端, Struts2框架提供了两种方式,进行提示信息。 addFieldError() 方法和 addActionError() 方法。

一. addFieldError() 方法和addActionError()方法

一.一 简单小提示

addFiledError(String name,String message) 添加字段范围内的具体的错误。

addActionError(String message) 在Action中添加具体的错误。

在Servlet开发阶段时,返回错误信息 通常用的是 request.setAttribute("message","具体的错误信息"),

现在可以用addFieldError()来进行处理。

对于类中的错误,用addActionError()进行处理。

两者所反映的级别是不一样的. field为字段级别的,action为Action级别的。

一.二 提示错误小例子

一.二.一 编写验证 UserAction

package com.yjl.web.action;
import com.opensymphony.xwork2.ActionSupport;
/**
* @author 两个蝴蝶飞
* @version 创建时间:Aug 30, 2018 8:21:09 PM
* User的验证类
*/
public class UserAction extends ActionSupport{
	private static final long serialVersionUID = 1L;
	/**
	 * 用addFiledError()方法处理
	 * 也是用key--value的形式进行展示的.
	 */
	public String field(){
		this.addFieldError("nameErr","姓名只能是中文");
		this.addFieldError("sexErr","性别只能选择男或者女");
		this.addFieldError("passwordErr","密码错误");
		return SUCCESS;
	}
	/**
	 *用 addActionError()方法处理。
	 */
	public String action(){
		//通常只写一个错误提示 
		this.addActionError("第一个类中大错误");
		return SUCCESS;
	}
}

一.二.二 配置struts.xml文件

<!--修改ui样式表,不用这一个,用默认的 -->
<!--<constant name="struts.ui.theme" value="simple"></constant>-->
<package name="validate" extends="struts-default" namespace="/">
		
		<action name="User_*" class="com.yjl.web.action.UserAction" method="{1}">
				<result name="success">/WEB-INF/content/message.jsp</result>
		</action>
		<!-- 配置跳转页面 -->
		<action name="*">
			<result>/WEB-INF/content/{1}.jsp</result>
		</action>
</package>

一.二.三 编写 /content/message.jsp

<body>
<h3>显示Field错误</h3>
<!-- 会显示全部的错误 -->
<s:fielderror></s:fielderror>
<h3>显示单个错误</h3>
<s:fielderror>
	<!-- 只显示name的错误信息 -->
	<s:param>nameErr</s:param>
</s:fielderror>

<h3>显示Action错误</h3>
<s:actionerror/>

</body>

一.二.四 重启服务器,运行

输入网址: http://localhost:8080/Struts_Validate/User_field, 显示field 域错误:

在这里插入图片描述

输入网址: http://localhost:8080/Struts_Validate/User_action, 显示action 域错误

有图片

显示字段错误时,如用户名 name是否填写错误,密码 password是否填写错误,手机号 phone是否填写错误,通常会显示全部的错误信息。

故一般常用:

<s:fielderror></s:fielderror>

来直接显示所有字段的错误。

一.二.五 hasFieldErrors() 和 hasActionErrors() 进行判断显示

访问 filed 错误时,没有action 错误,但却将Action的提示语都显示出来了,

访问action 错误时,没有filed 错误,但却将 Field的提示语都 显示出来了。

常常需要在 显示错误之前,进行一下判断, 如果有错误,才显示,如果没有错误的话,就不显示 。

用 hasFieldErrors() 用于验证是否有 field 级别的错误。

用 hasActionErrors() 用于验证是否有 action 级别的错误。

一.二.六 重写 /content/message.jsp 页面, 添加判断

<body>
<!-- 判断一下,是否有field 范围的错误 -->
<s:if test="hasFieldErrors()">
	<h3>显示Field错误</h3>
	<!-- 会显示全部的错误 -->
	<s:fielderror></s:fielderror>
	<h3>显示单个错误</h3>
	<s:fielderror cssErrorStyle="color:red;font-size:20px;">
		<!-- 只显示name的错误信息 -->
		<s:param>nameErr</s:param>
	</s:fielderror>
</s:if>
<!--判断一下,是否有 action范围的错误 -->
<s:if test="hasActionErrors()">
	<h3>显示Action错误</h3>
	<s:actionerror/>
</s:if>
</body>

再次进行测试:

输入网址: http://localhost:8080/Struts_Validate/User_field, 显示field 域错误:

有图片

输入网址: http://localhost:8080/Struts_Validate/User_action, 显示action 域错误

有图片

二 业务方法中验证

我们希望用户在前端表单填写数据,进行提交,如果错误了,就返回相应的错误信息,如果没有任何错误,就通过。

业务方法验证可以分为三个部分。

二.一 开发者自己手动验证

如用户登录时的验证:

//第一种情况: 在登录方法中直接进行验证
public String login(){
	//1. 接收到各个数据,如用户名,密码,邮箱等.
	String userName="两个蝴蝶飞";
	String password="yjl";
	String email="1290513799@qq.com";
	//2. 判断用户名是否错误,如果出现了错误,就this.addFieldError("userName","用户名错误");
	if(!"两个蝴蝶飞".equals(userName)){
		this.addFieldError("userName","用户名错误");
	}
	//3. 判断密码是否错误,如果出现了错误,就this.addFieldError("password","密码错误");
	if(!"yjl".equals(password)){
		this.addFieldError("password","密码错误");
	}
	//4. 判断邮箱是否错误,如果出现了错误,就this.addFieldError("email","邮箱错误");
	if(!"1290513799@qq.com".equals(email)){
		this.addFieldError("email","邮箱错误");
	}
	//5. 判断在这个action中是否出现了错误,如果有错误,将其添加到Action中,返回输入的界面
	if(this.hasErrors()){ 
	 //也有hasFieldErrors()和hasActionErrors(), hasErrors()包括field错误和action错误
		this.addActionError("登录的输入数据不对");
		return "loginUI";
	}
	//6.现在才是进行正常的逻辑操作,上面都是数据的检测(第一步获取数据除外)
	//判断是否登录成功,如果成功返回到成功界面, 如果不成功,返回到登录的界面,
	//... 登录业务
	return SUCCESS;
}

需要我们自己写验证代码,且不具有通过性,每一个方法,如注册方法register() 也需要进行验证。

二.二 重写 validate() 方法

二.二.一 Validateable 接口定义

ActionSupport 的类定义如下:

public class ActionSupport
implements Action, Validateable, ValidationAware, TextProvider, LocaleProvider, Serializable{
	
}

里面实现了一个验证接口 Validateable
该接口 Validateable 里面有一个validate()方法

public interface Validateable {

    /**
     * Performs validation.
     */
    void validate();

}

UserAction 继承了ActionSupport类,故继承了validate()方法,开发者 需要重写validate()方法即可。

数据验证,写在了 validate() 方法里面,正常的登录代码写在了 login() 方法里面,

数据验证与业务逻辑进行了分离。

那么此时,登录的逻辑判断就可以写成:

二.二.二 重写 validate() 方法进行验证

//第二种方式,重写validate()方法进行验证
public String login(){
	//1. 接收到各个数据,如用户名,密码,邮箱等.
	String userName="两个蝴蝶飞";
	String password="yjl";
	String email="1290513799@qq.com";
	
	//5. 判断在这个action中是否出现了错误,如果有错误,将其添加到Action中,返回输入的界面
	if(this.hasErrors()){ //也有hasFieldErrors()和hasActionErrors(), 这是两个方法的错误相加
		this.addActionError("登录的输入数据不对");
		return "loginUI";
	}
	//直接判断型业务逻辑,不用管数据验证。(当然前面有一个检测的操作)
	//判断是否登录成功,如果成功返回到成功界面, 如果不成功,返回到登录的界面,
	//用addFieldError或者addActionError添加错误信息
	return SUCCESS;
}
@Override
public void validate() {
	//只关注数据的验证
	//1. 接收到各个数据,如用户名,密码,邮箱等.
	String userName="两个蝴蝶飞";
	String password="yjl";
	String email="1290513799@qq.com";
	//2. 判断用户名是否错误,如果出现了错误,就this.addFieldError("userName","用户名错误");
	if(!"两个蝴蝶飞".equals(userName)){
		this.addFieldError("userName","用户名错误");
	}
	//3. 判断密码是否错误,如果出现了错误,就this.addFieldError("password","密码错误");
	if(!"yjl".equals(password)){
		this.addFieldError("password","密码错误");
	}
	//4. 判断邮箱是否错误,如果出现了错误,就this.addFieldError("email","邮箱错误");
	if(!"1290513799@qq.com".equals(email)){
		this.addFieldError("email","邮箱错误");
	}
}

二.二.三 流程处理

在进入login()方法之前,首先会调用validate()方法,进行数据验证。

相比较第一种方式,这个时候便处理上简单了一些。

但还是有一些特别严重的问题。 那就是在访问UserAction时,在进入每一个方法之前,都会先调用validate()方法。

如: UserAction 中 还存在 toLogin 和 register() 方法:

    public String toLogin(){
		//先调用validate()方法
		return "login";
	}
	public String register(){
		//先调用validate()方法
		return "register";
	}

然而,实际生活中, 登录时的验证与注册时的验证,验证数据的多少是不一样的,如注册时验证性别不为空,居住地不为空,
在登录时是不需要验证的,还有确认密码登录时是没有的。
更有的情况,登录和注册时对同一数据的验证规则是不一样的(根据具体业务情况分析),
如注册时需要验证用户名是否存在,登录时不需要验证用户名是否存在。
这个时候,需要在validate()方法中进行大量的判断,如果方法名login,走哪个验证,是register(),起哪个验证,是toLogin()的话,不走验证。

二.三 针对单个方法分别进行验证

需要注意,这个时候,验证数据的方法名称必须符合validateXxx()的形式。

如果是对login() 方法进行验证,那么验证login数据是否正确的方法名称必须是 validateLogin(),

如果是对register() 方法进行验证,那么验证reigster 数据是否正确的方法名称必须是 validateRegister().

对login()和register()方法分别进行验证:

public String login(){
	if(this.hasErrors()){
		this.addActionError("登录的输入数据不对");
		return "loginUI";
	}
	return "login";
}
public String register(){
	if(this.hasErrors()){
		this.addActionError("注册的输入数据不对");
		return "loginUI";
	}
	return "register";
}
public void validateLogin(){
	//对登录表单中的数据进行验证
}
public void validateRegister(){
	//对注册表单中的数据进行验证
}

有人会问,在注册时出现了错误,已经将数据放入到Action中了,即hasErrors()为true了,那么进入login()方法,取出的数据不就是true吗? 这是一个误区。
Strtus2是多实例的,在进入register()方法时,会重新实例化对象,所有的数据都会清空(静态属性和代码块除外),包括错误信息。
在进入login()时,也会重新实例化一下,会清空的。 需要设置 scope="prototype"。

这种方式相对于第二种来说,可读性大大增强,但每一个方法都要验证数据,都需要写一个validateXxx()方法,会导致验证方法过多。

注意,当Action既有 validate()方法,也有validateLogin()方法时,如果执行的是login操作时,
那么会先执行validateLogin()方法,
再执行validate()方法,有一个先后的顺序,但是两者均起作用的。

(图片摘自森林森前辈的Struts2(十二),本章图片均来自于此,谢谢。
源址为:https://www.cnblogs.com/liunanjava/p/4383237.html

在这里插入图片描述

常常用 Validate 框架进行验证。

三. validate框架验证数据

使用验证框架的好处, 1.验证实现的部分方便复用和扩展,2.验证的实现和业务处理分离。

验证里的配置需要在 xml文件中进行相应的配置。

这里验证一些常见的属性,用User类进行相应的包装。

三.一 未使用框架验证之前

三.一.一 编写 User.java

/**
* @author 两个蝴蝶飞
* @version 创建时间:Sep 8, 2018 2:46:59 PM
* 用于包装框架数据的实体类,没有实际意义
*/
public class User {
	/**
	 * @param id 编号id
	 * @param userName 用户名
	 * @param password 密码
	 * @param name 真实姓名
	 * @param sex 性别
	 * @param age 年龄
	 */
	private Integer id;
	private String userName;
	private String password;
	private String name;
	private String sex;
	private int age;
}

属性需要实现setter和getter方法,也添加一个 toString() 方法。

三.一.二 编写 RegisterAction 类

里面定义跳转到注册和注册的两个方法:

/**
* @author 两个蝴蝶飞
* @version 创建时间:Aug 30, 2018 8:21:09 PM
* User的相关验证
*/
public class RegisterAction extends ActionSupport{
	private static final long serialVersionUID = 1L;
	private static Logger logger=Logger.getLogger(UserAction.class);
	private User user;
	public User getUser() {
		return user;
	}
	public void setUser(User user) {
		this.user = user;
	}
	public String toRegister(){
		logger.info("跳转到注册页面");
		return "registerUI";
	}
	public String register(){
		logger.info("执行注册的功能");
		logger.info("用户名是:"+user.getUserName()+",密码是:"+user.getPassword());
		return "register";
	}
}

三.一.三 配置 struts.xml

<action name="Register_*" class="com.yjl.web.action.RegisterAction" method="{1}">
			<result name="registerUI">/WEB-INF/content/register.jsp</result>
			<result name="register">/WEB-INF/content/success.jsp</result>
			<!--错误后,跳转到此页面-->
			<result name="input">/WEB-INF/content/register.jsp</result>
</action>

三.一.四 编写 /content/register.jsp

<body>
	<h3>这是一个注册框架的页面</h3>
    <s:if test="hasFieldErrors()">
		<s:fielderror></s:fielderror>
	</s:if>
	<s:form action="Register_register" method="post" namespace="/">
		<s:textfield label="编号" name="user.id"></s:textfield>
		<s:textfield label="用户名" name="user.userName"></s:textfield>
		<s:textfield label="密码" name="user.password"></s:textfield>
		<s:textfield label="确认密码" name="repassword"></s:textfield>
		<s:textfield label="真实姓名" name="user.name"></s:textfield>
		<s:radio label="性别" list="#{'m':'男','f':'女'}" name="user.sex"></s:radio>
		<s:textfield label="年龄" name="user.age"></s:textfield>
		<s:submit value="提交"/>
	</s:form>
</body>

/content/success.jsp 的内容显示 注册成功

<body>
	注册成功
</body>

三.一.五 重启服务器,进行验证

网址是: http://localhost:8080/Struts_Validate/Register_toRegister

在这里插入图片描述

点击提交,页面正常跳转


在这里插入图片描述

日志打印输出:


在这里插入图片描述


这个时候,环境已经搭建成功了。

现在并没有进行相应的验证操作。

接下来就是具体的验证。

三.二 validate验证框架的具体详细使用

三.二.一 创建 Xxx.xml 验证文件

在action包下,创建 .xml 配置文件,该配置文件的文件名称 action名-validation.xml。

如UserAction的验证配置文件是UserAction-validation.xml,注意连接符是-,并不是下划线_

该例子用的是 RegisterAction, 那么创建的配置文件名称就是 RegisterAction-validation.xml

有图片

三.二.二 配置约束

在UserAction-validation.xml中引入相关的约束

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.2//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
	<!-- 里面写具体的验证描述 -->
</validators>

三.二.三 添加id 的约束

<!-- field中的name指定的是要验证的哪一个属性-->
<field name="user.id"> 
		<!-- field-validator 中type指定类型 -->
		<field-validator type="required"> 
			<!-- message指定错误的信息 -->
			<message>必须存在id</message> 
		</field-validator>
</field>

三.二.四 重启服务器,验证

重启服务器,发现出错了。

再次输入网址: http://localhost:8080/Struts_Validate/Register_toRegister

有图片

提示,必须输入id的值。

当我们在前端 get 方式传入id的值后, (注意,传入的是user.id的值)

有图片

没有提示任何错误,可以正确的进行跳转到注册的页面了。

当我们输入一些值,但不输入编号时:

有图片

点击提交按钮后,

有图片

说明,验证框架起作用了,验证了 id不能为空。

三.二.五 验证 user.name的类型

配置user.userName 必须是字符串类型,且不能为空

在RegisterAction-validation.xml 中配置user.userName的验证

<!-- 配置user.userName的类型 -->
	 <field name="user.userName">
		<field-validator type="requiredstring">
			<message>用户名不能为空,且必须是字母类型</message>
		</field-validator>
</field>

重启服务器,进行测试,验证框架也同样起作用(这儿就不贴图了)

三.二.六 验证user.password

必须是字符串类型,且有长度限制。

在RegisterAction-validation.xml 中配置user.password 的验证

    <field name="user.password">
		<field-validator type="requiredstring">
			<message>密码不能为空</message>
		</field-validator>
		<!-- 多添加一个验证 -->
		<field-validator type="stringlength">
			<!-- 去除两边的空格 -->
			<param name="trim">true</param>
			<!-- 设置最小和最大的长度, 注意是大写Length -->
			<param name="minLength">6</param>
			<param name="maxLength">15</param>
			<!-- 设置显示的信息  可以用ognl表达式来显示-->
			<message>密码的长度应该在${minLength}~${maxLength}之间</message>
		</field-validator>
	</field>

可以在一个字段中,添加多个验证。 注意参数trim,maxLength,minLength的写法。

验证长度用type="stringlength" 这里是小写。

三.二.七 配置确认密码与user.password密码值一样

常用于字段表达式

前端jsp 设置:

 <s:textfield label="密码" name="user.password"></s:textfield>
 <s:textfield label="确认密码" name="repassword"></s:textfield>

在RegisterAction-validation.xml 中配置repassword 的验证

<field name="repassword">
		<field-validator type="fieldexpression" short-circuit="true">
			<param name="trim">true</param>
			<param name="expression"><![CDATA[(#parameters.password[0] == #parameters.repassword[0])]]></param>
			<message>确认密码必须与原先密码相同</message>
		</field-validator>
</field>

这个时候,才算是达到了要达到的效果。

三.二.八 配置真实姓名的中文验证

必须是2~4个中文,用regex类型,演示正则表达式

<field name="user.name">
		<field-validator type="regex">
			<!-- 记住中文的验证规则 -->
			<param name="regexExpression"><![CDATA[([u4E00-u9FA5]{2,4})]]></param>
			<message>必须是两个汉字~四个汉字</message>
		</field-validator>
	</field>

三.二.九 对age的int类型配置

需要指定其最大和最小值 用int

<field name="user.age">
	 	<field-validator type="int">
	 		<!-- 最小值和最大值 min,max-->
	 		<param name="min">18</param>
	 		<param name="max">58</param>
	 		<message>注册年龄必须在${min}~${max}岁之间</message>
	 	</field-validator>
	</field>

三.二.十 对money的double类型配置

用double类型

<field name="user.money">
	 	<field-validator type="double">
	 		<message>金钱必须是小数</message>
	 	</field-validator>
	</field>

输入整数或者浮点数是可以通过,但是输入字母是出错的。 另

外,输入1.2d,或者1.2f是正确的。

如果出错,显示的信息是:

在这里插入图片描述

三.三 验证文件所在的位置

在UserAction-validator.xml验证文件中,那些type="int",type="required",并不是随便写的,而是已经定义好的。它在

Struts2下载目录Struts2srcappsportletsrcmain esources目录下

在这里插入图片描述

该文件,其中内容主要是:

validators.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
        "-//Apache Struts//XWork Validator Config 1.0//EN"
        "http://struts.apache.org/dtds/xwork-validator-config-1.0.dtd">
<validators>
    <validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/>
    <validator name="requiredstring" class="com.opensymphony.xwork2.validator.validators.RequiredStringValidator"/>
    <validator name="int" class="com.opensymphony.xwork2.validator.validators.IntRangeFieldValidator"/>
    <validator name="short" class="com.opensymphony.xwork2.validator.validators.ShortRangeFieldValidator"/>
    <validator name="double" class="com.opensymphony.xwork2.validator.validators.DoubleRangeFieldValidator"/>
    <validator name="date" class="com.opensymphony.xwork2.validator.validators.DateRangeFieldValidator"/>
    <validator name="expression" class="com.opensymphony.xwork2.validator.validators.ExpressionValidator"/>
    <validator name="fieldexpression" class="com.opensymphony.xwork2.validator.validators.FieldExpressionValidator"/>
    <validator name="email" class="com.opensymphony.xwork2.validator.validators.EmailValidator"/>
    <validator name="url" class="com.opensymphony.xwork2.validator.validators.URLValidator"/>
    <validator name="visitor" class="com.opensymphony.xwork2.validator.validators.VisitorFieldValidator"/>
    <validator name="conversion" class="com.opensymphony.xwork2.validator.validators.ConversionErrorFieldValidator"/>
</validators>

可以点击进入查看源代码,其中<param></param>中就是那些属性。

在这里插入图片描述

四. 为每一个方法写单独验证,

上面t每一个Action写了一个xml验证文件,这个是相对于整个Action来说的。

即这个Action中的每一个方法,都会进行这些统一的验证。

做一个简单的小例子。

在UserAction中添加一个转到登录的方法,在Action 写入方法,

public String toLogin(){
		logger.info("跳转到登录的页面");
		return "toLogin";
	}

其相应的 struts.xml 配置文件 :

<result name="toLogin">/login.jsp</result>

访问toLogin方法时: http://localhost:8090/Struts_validate/User_toLogin.action

会跳转到注册的页面,(跳转到注册页面是因为出错了,配置了result 中name="input")

日志也没有输出

在这里插入图片描述

在执行toLogin()方法之前,也对user.id,user.name 等属性 进行了相应的验证。

四.一 创建业务性验证

在与要验证的Action同级目录下,创建一个验证文件,命名方式必须是: ActionName-要验证方法名-validation.xml。

故新建一个UserAction-register-validation.xml文件,添加上面的一些验证,原先的UserAction-validation.xml文件保留,但去除验证配置。
在这里插入图片描述

重启服务器,进入http://localhost:8090/Struts_validate/User_toLogin.action,发现可以正常进入,会跳转到相应的login.jsp页面,而且日志也可以打印输出。

在这里插入图片描述

这样就完成了单个业务验证的处理。

有时候,一个Action中有很多方法,特别是toXXX页面的方法,这个时候就不建议对整个Action进行验证了,而是对每一个方法即业务进行单独的验证。但可能会造成一个Action中的验证文件过多的情况。根据业务需求进行相应的取舍。

即如果是多个方法,可以:

在这里插入图片描述

对add,login,register 三个方法进行单独的验证。

五. 验证框架的图像总结

验证顺序:

在这里插入图片描述

验证知识点总结

在这里插入图片描述

本章节代码链接为:

链接:https://pan.baidu.com/s/1_8Uo0YgGt9DLh1GRum0_uw 
提取码:yi71 

谢谢您的观看!!!

原文地址:https://www.cnblogs.com/yjltx/p/13071765.html