Struts2验证框架
1.使用validate()方法校验
在Struts2框架中,validate()方法是专门用来校验数据的方法,具体实现时可以通过继承ActionSupport类,并重写validate方法完成校验。示例如下:
- 先建立一个Member的简单Java类,并覆写toString()方法。
import java.io.Serializable;
import java.util.Date;
@SuppressWarnings("serial")
public class Member implements Serializable {
private Integer id;
private String name;
private Double salary;
private Date credate;
public Member() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
public Date getCredate() {
return credate;
}
public void setCredate(Date credate) {
this.credate = credate;
}
@Override
public String toString() {
return "Member{" +
"id=" + id +
", name='" + name + '\'' +
", salary=" + salary +
", credate=" + credate +
'}';
}
}
- 建立MemberAction类接受数据并进行验证(此处为方便只编写了空验证)。
import com.bank.vo.Member;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.convention.annotation.*;
@ParentPackage("root")//表示继承了root父包
@Namespace(value="/pages/member")//定义了自己的命名空间
@Action(value="MemberAction")//定义了Action的访问名称
//配置了发生错误时的跳转页面为根目录下的/pages/errors.jsp
@Results(@Result(name="input",location ="/pages/errors.jsp",type="dispatcher"))
public class MemberAction extends ActionSupport {
private Member member;
public Member getMember() {
return member;
}
public void setMember(Member member) {
this.member = member;
}
@Override
public void validate() {
if(this.member.getId()==null){
super.addFieldError("member.id","用户编号不能为空");
}
if(this.member.getName()==null||"".equals(this.member.getName())){
super.addFieldError("member.name","用户名不能为空");
}
if(this.member.getSalary()==null){
super.addFieldError("member.salary","工资不能为空");
}
if(this.member.getCredate()==null){
super.addFieldError("member.credate","注册日期不能为空");
}
}
public void test(){
System.out.println(this.member);
}
}
- 在/pages/member目录下新建member.jsp页面,编写提交表单
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page pageEncoding="UTF-8" %>
<html>
<head>
<title>Member</title>
</head>
<body>
<form action="MemberAction!test.action" method="post">
用户编号:<input type="text" id="member.id" name="member.id" /><br>
用户名:<input type="text" id="member.name" name="member.name" /><br>
工资:<input type="text" id="member.salary" name="member.salary" /><br>
注册日期:<input type="text" id="member.credate" name="member.credate" /><br>
<input type="submit" value="提交" />
</form>
</body>
</html>
- 在/pages目录下新建errors.jsp页面显示错误信息
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page pageEncoding="UTF-8" %>
<html>
<head>
<title>Errors</title>
</head>
<body>
<h1>Errors</h1>
错误信息:${fieldErrors}
</body>
</html>
- 随后我们运行程序找到表单后不填写内容直接提交,就会跳转到errors.jsp显示错误信息
注意:如果一个在Action中采用分发的处理方式(即一个Action中编写多个方法),如果我们只想对某一个方法进行数据校验,其他的方法不需要校验,我们就可以便编写validateXxx()方法,Xxx为方法名,即我们编写的validateXxx方法只对Xxx方法进行数据校验。在上面的实例中,我们只需要将validate()方法变为validateTest()方法,就可以只针对test()方法进行数据校验。
public void validateTest() {
if(this.member.getId()==null){
super.addFieldError("member.id","用户编号不能为空");
}
if(this.member.getName()==null||"".equals(this.member.getName())){
super.addFieldError("member.name","用户名不能为空");
}
if(this.member.getSalary()==null){
super.addFieldError("member.salary","工资不能为空");
}
if(this.member.getCredate()==null){
super.addFieldError("member.credate","注册日期不能为空");
}
}
2.使用配置文件的方式进行数据校验
使用validate()方法和validateXxx()方法虽然可以完成数据校验的任务,但是将校验嵌入到Action类之中会使Action类变得非常复杂和臃肿,同时增加Action和输入校验之间的耦合度,使得开发变得不方便。因此Struts2提供了一种基于框架的校验方式,将校验规则保存在文件中,使Action和校验分离,提高了系统的维护性和扩展性。使用验证框架完善上面历程中的功能。
- 在包含Action类的包中定义ActionName-validation.xml或ActionName-alias-validation.xml文件(定义的校验文件一定要与Action类放在同一目录下,ActionName表示实际的Action类名,alias表示struts.xml文件或注解中配置的Action访问名字) ,验证文件的结构如下图所示:
- 标记为( + )的元素:该元素可以使用一次或者多次。
- 标记为( * )的元素:该元素可以使用0次或者多次。
- 没有标记的元素:该元素是必须的。
- 解压xwork-x.x.x.jar文件,在xwork-x.x.x\com\opensymphony\xwork2\validator\validators目录下,可以找到default.xml文件,该XML文件定义了Struts2框架的内建校验器,通过这些内建校验器的使用,可以很方便的实现各种数据的校验。该文件如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator Definition 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-definition-1.0.dtd">
<!-- START SNIPPET: validators-default -->
<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="long" class="com.opensymphony.xwork2.validator.validators.LongRangeFieldValidator"/>
<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"/>
<validator name="stringlength" class="com.opensymphony.xwork2.validator.validators.StringLengthFieldValidator"/>
<validator name="regex" class="com.opensymphony.xwork2.validator.validators.RegexFieldValidator"/>
<validator name="conditionalvisitor" class="com.opensymphony.xwork2.validator.validators.ConditionalVisitorFieldValidator"/>
</validators>
<!-- END SNIPPET: validators-default -->
- 使用验证框架对上例的校验进行修改,新建MemberAction.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>
<field name="member.id">
<field-validator type="required">
<message>用户编号不许为空</message>
</field-validator>
<field-validator type="int">
<message>用户编号必须为数字</message>
</field-validator>
</field>
<field name="member.name">
<field-validator type="requiredstring">
<message>用户名不许为空</message>
</field-validator>
<field-validator type="stringlength">
<param name="maxLength">10</param>
<param name="minLength">4</param>
<message>用户名长度在${minLength}-${maxLength}之间</message>
</field-validator>
</field>
<field name="member.salary">
<field-validator type="required">
<message>工资不许为空</message>
</field-validator>
<field-validator type="double">
<message>工资必须为数字</message>
</field-validator>
</field>
<field name="member.credate">
<field-validator type="required">
<message>日期不许为空</message>
</field-validator>
<field-validator type="date">
<param name="min">1998-1-3</param>
<param name="max">2020-5-1</param>
<message>日期应在${min}-${max}之间</message>
</field-validator>
</field>
</validators>
3.使用拦截器编写自己的验证框架
Struts2中拦截器的实现原理如下图所示:
Struts2中的自动赋值功能是由名叫DefaultStack的拦截器实现的,如果我们要进行数据的验证,则应在赋值之前进行,以防止类型转换出现错误。
- 首先定义自己的验证工具类,应包含对常用数据类型的验证,如空验证、整数验证、小数验证、日期验证等。
public class ValidateUtil {
/**
* 验证数据是否非空
* @param data 要验证的数据
* @return 若数据非空,返回true,否则返回false
*/
public static boolean validateEmpty(String data){
return data!=null&&data.length()>0;
}
/**
* 验证数据是否满足正则要求
* @param data 要验证的数据
* @param regex 正则表达式
* @return 若数据满足正则返回true,否则返回false
*/
public static boolean validateRegex(String data,String regex){
if(validateEmpty(data)){
return data.matches(regex);
}
return false;
}
/**
* 验证数据是否为整数
* @param data 要验证的数据
* @return 若数据为整数返回true,否则返回false
*/
public static boolean validateInt(String data){
return validateRegex(data,"\\d+");
}
/**
* 验证数据是否为数字
* @param data 要验证的数据
* @return 若数据为数字返回true,否则返回false
*/
public static boolean validateNum(String data){
return validateRegex(data,"\\d+(\\.\\d+)?");
}
/**
* 验证数据是否为日期
* @param data 要验证的数据
* @return 若数据为日期返回true,否则返回false
*/
public static boolean validateDate(String data){
//判断是否满足日期格式
if(validateRegex(data,"\\d{4}-\\d{1,2}-\\d{1,2}")){
return true;
}else{
//判断是否满足日期时间格式
return validateRegex(data,"\\d{4}-\\d{1,2}-\\d{1,2} \\d{2}:\\d{2}:\\d{2}");
}
}
}
2.编写数据验证拦截器。
import com.bank.util.StrutsValidate;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import org.apache.struts2.ServletActionContext;
import java.lang.reflect.Field;
import java.util.Map;
public class ValidateInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation actionInvocation) throws Exception {
Object actionObj = actionInvocation.getAction();//取得要访问的Action类对象
String uri = ServletActionContext.getRequest().getRequestURI();//取得请求的相对地址
uri = uri.substring(uri.indexOf("!")+1);
uri = uri.substring(0,uri.indexOf("."))+"VF";//取得验证规则属性名称
Field ruleField = actionObj.getClass().getDeclaredField(uri);//取得包含验证规则的属性
ruleField.setAccessible(true);//解除属性操作禁制
String rule = (String)ruleField.get(actionObj);//取得验证规则
//取的请求的所有参数名称和值(包含要验证的数据)
Map<String,Object> params = actionInvocation.getInvocationContext().getParameters();
//将验证操作交给StrutsValidate类来完成
StrutsValidate sv = new StrutsValidate(actionObj,rule,params);
if(sv.validate()){//验证通过则请求继续传递
return actionInvocation.invoke();
}else{//验证失败跳转到显示错误的页面进行错误显示
return ActionSupport.INPUT;
}
}
}
- 编写StrutsValidate类,接收要访问的Action类对象、验证规则和要验证的数据,通过validate方法进行验证。
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class StrutsValidate {
private Object actionObj;//表示要触发此操作的Action类
private String rule;//每个Action类里定义的规则
private Map<String,Object> params;//所有页面传递来的数据(包含要验证的数据)
private Map<String,String> errors = new HashMap<String,String>();//保存所有的错误信息
public StrutsValidate(Object actionObj,String rule,Map<String,Object> params){
this.actionObj = actionObj;
this.rule = rule;
this.params = params;
}
public boolean validate(){
//如果验证规则为空,则表示没有数据要进行验证,验证可以直接通过
if(!ValidateUtil.validateEmpty(this.rule)){
return true;
}
try{
//通过此方法取得错误的提示信息
Method getTextMethod = this.actionObj.getClass().getMethod("getText",String.class);
//取得增加错误信息的方法,通过此方法保存错误信息
Method addFieldErrorMethod = this.actionObj.getClass().getMethod("addFieldError",String.class,String.class);
//所有的验证操作都应该由rule发起,里面的组成是 参数名称:类型
String[] results = rule.split("\\|");//取出每一组验证规则
for(int x=0;x<results.length;x++){//循环每一个验证
String paramName = results[x].split(":")[0];//取得参数名字
String paramType = results[x].split(":")[1];//取得参数验证类型
String[] datas = (String[])this.params.get(paramName);//取得参数值
String errorTie = null;//保存每次的错误提示信息
for(int y=0;y<datas.length;y++){//进行有效的验证
if("string".equalsIgnoreCase(paramType)){
if(!ValidateUtil.validateEmpty(datas[y])){
errorTie = (String) getTextMethod.invoke(this.actionObj,"validate.string");
}
}else if("integer".equalsIgnoreCase(paramType)||"int".equalsIgnoreCase(paramType)){
if(!ValidateUtil.validateInt(datas[y])){
errorTie = (String) getTextMethod.invoke(this.actionObj,"validate.int");
}
}else if("double".equalsIgnoreCase(paramType)){
if(!ValidateUtil.validateNum(datas[y])){
errorTie = (String) getTextMethod.invoke(this.actionObj,"validate.double");
}
}else if("date".equalsIgnoreCase(paramType)){
if(!ValidateUtil.validateDate(datas[y])){
errorTie = (String) getTextMethod.invoke(this.actionObj,"validate.date");
}
}
if(errorTie!=null){
//添加错误信息
this.errors.put(results[x],errorTie);
addFieldErrorMethod.invoke(this.actionObj,results[x],errorTie);
}
errorTie = null;//错误信息重置
}
}
if(this.errors.size()==0){
return true;
}
return false;
}catch(Exception e){
e.printStackTrace();
}
return true;
}
public Map<String,String> getErrors(){
return this.errors;
}
}
- 新建资源文件,写入验证失败时的提示信息
validate.string=不许为空!
validate.int=必须为整数!
validate.double=必须为数字!
validate.date=必须为日期!
- 修改Action类,在里面加入验证规则,并启用拦截器
import com.bank.vo.Member;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.convention.annotation.*;
@ParentPackage("root")
@Namespace(value="/pages/member")
@Action(value="MemberAction")
@InterceptorRefs(value={
@InterceptorRef(value="ValidateInterceptor"),//启用我们自己编写的验证拦截器
@InterceptorRef(value="defaultStack")//启用Struts2的自动赋值功能
})
@Results(@Result(name="input",location ="/pages/errors.jsp",type="dispatcher"))
public class MemberAction extends ActionSupport {
//编写验证规则
private String testVF = "member.id:integer|member.name:string|" +
"member.salary:double|member.credate:date";
private Member member;
public Member getMember() {
return member;
}
public void setMember(Member member) {
this.member = member;
}
public void test(){
System.out.println(this.member);
}
}
这时再使用表单访问此Action,就会自动启用自己便编写的验证了。