勿以恶小而为之,勿以善小而不为--------------------------刘备
劝诸君,多行善事积福报,莫作恶
上一章简单介绍了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文件
/WEB-INF/content/message.jsp
/WEB-INF/content/{1}.jsp
一.二.三 编写 /content/message.jsp
显示Field错误
显示单个错误
nameErr
显示Action错误
一.二.四 重启服务器,运行
输入网址: http://localhost:8080/Struts_Validate/User_field, 显示field 域错误:
输入网址: http://localhost:8080/Struts_Validate/User_action, 显示action 域错误
显示字段错误时,如用户名 name是否填写错误,密码 password是否填写错误,手机号 phone是否填写错误,通常会显示全部的错误信息。
故一般常用:
来直接显示所有字段的错误。
一.二.五 hasFieldErrors() 和 hasActionErrors() 进行判断显示
访问 filed 错误时,没有action 错误,但却将Action的提示语都显示出来了,
访问action 错误时,没有filed 错误,但却将 Field的提示语都 显示出来了。
常常需要在 显示错误之前,进行一下判断, 如果有错误,才显示,如果没有错误的话,就不显示 。
用 hasFieldErrors() 用于验证是否有 field 级别的错误。
用 hasActionErrors() 用于验证是否有 action 级别的错误。
一.二.六 重写 /content/message.jsp 页面, 添加判断
显示Field错误
显示单个错误
nameErr
显示Action错误
再次进行测试:
输入网址: 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="[email protected]";
//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(!"[email protected]".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="[email protected]";
//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="[email protected]";
//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(!"[email protected]".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
/WEB-INF/content/register.jsp
/WEB-INF/content/success.jsp
/WEB-INF/content/register.jsp
三.一.四 编写 /content/register.jsp
这是一个注册框架的页面
/content/success.jsp 的内容显示 注册成功
注册成功
三.一.五 重启服务器,进行验证
网址是: 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中引入相关的约束
三.二.三 添加id 的约束
必须存在id
三.二.四 重启服务器,验证
重启服务器,发现出错了。
再次输入网址: http://localhost:8080/Struts_Validate/Register_toRegister
提示,必须输入id的值。
当我们在前端 get 方式传入id的值后, (注意,传入的是user.id的值)
没有提示任何错误,可以正确的进行跳转到注册的页面了。
当我们输入一些值,但不输入编号时:
点击提交按钮后,
说明,验证框架起作用了,验证了 id不能为空。
三.二.五 验证 user.name的类型
配置user.userName 必须是字符串类型,且不能为空
在RegisterAction-validation.xml 中配置user.userName的验证
用户名不能为空,且必须是字母类型
重启服务器,进行测试,验证框架也同样起作用(这儿就不贴图了)
三.二.六 验证user.password
必须是字符串类型,且有长度限制。
在RegisterAction-validation.xml 中配置user.password 的验证
密码不能为空
true
6
15
密码的长度应该在${minLength}~${maxLength}之间
可以在一个字段中,添加多个验证。 注意参数trim,maxLength,minLength的写法。
验证长度用type="stringlength" 这里是小写。
三.二.七 配置确认密码与user.password密码值一样
常用于字段表达式
前端jsp 设置:
在RegisterAction-validation.xml 中配置repassword 的验证
true
确认密码必须与原先密码相同
这个时候,才算是达到了要达到的效果。
三.二.八 配置真实姓名的中文验证
必须是2~4个中文,用regex类型,演示正则表达式
必须是两个汉字~四个汉字
三.二.九 对age的int类型配置
需要指定其最大和最小值 用int
18
58
注册年龄必须在${min}~${max}岁之间
三.二.十 对money的double类型配置
用double类型
金钱必须是小数
输入整数或者浮点数是可以通过,但是输入字母是出错的。 另
外,输入1.2d,或者1.2f是正确的。
如果出错,显示的信息是:
三.三 验证文件所在的位置
在UserAction-validator.xml验证文件中,那些type="int",type="required",并不是随便写的,而是已经定义好的。它在
Struts2下载目录\Struts2\src\apps\portlet\src\main\resources目录下
该文件,其中内容主要是:
validators.xml文件:
可以点击进入查看源代码,其中中就是那些属性。
四. 为每一个方法写单独验证,
上面t每一个Action写了一个xml验证文件,这个是相对于整个Action来说的。
即这个Action中的每一个方法,都会进行这些统一的验证。
做一个简单的小例子。
在UserAction中添加一个转到登录的方法,在Action 写入方法,
public String toLogin(){
logger.info("跳转到登录的页面");
return "toLogin";
}
其相应的 struts.xml 配置文件 :
/login.jsp
访问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
谢谢您的观看!!!