SpringMVC整合Hibernate Validator验证方法与js校验

来源:互联网 发布:追风打印软件 编辑:程序博客网 时间:2024/06/07 07:23

     

SpringMVC服务器验证一种是有两种方式,一种是基于Validator接口,一种是使用Annotaion JSR-303标准的验证,下面主要是学习这两种,工作中推荐后者,方便很多

使用Annotaion JSR-303标准的验证,需要页面刷新才可以,即当点击提交时,错误才能显示。可以通过js(JavaScript)来实现立即显示错误。

JavaScript 表单验证

JavaScript 可用来在数据被送往服务器前对 HTML 表单中的这些输入数据进行验证。

<html><head><script type="text/javascript">function validate_required(field,alerttxt){with (field)  {  if (value==null||value=="")    {alert(alerttxt);return false}  else {return true}  }}function validate_form(thisform){with (thisform)  {  if (validate_required(email,"Email must be filled out!")==false)    {email.focus();return false}  }}</script></head><body><form action="submitpage.htm" onsubmit="return validate_form(this)" method="post">Email: <input type="text" name="email" size="30"><input type="submit" value="Submit"> </form></body></html>
<form action="/back/login/login" method="post" id="form1" onsubmit="return sb1();">

书写js验证规则:

 1 function sb1(){ 2         var username = document.getElementById("username"); 3         var password = document.getElementById("passsword"); 4         if(trim(username.value)==null || trim(username.value)==""){ 5             alert("请输入用户名"); 6             username.focus(); 7             return false; 8         } 9         if(trim(password.value)==null || trim(password.value)==""){10             alert("请输入密码");11             password.focus();12             return false;13         }14         15         return true;16     }17     function trim(str){ //删除左右两端的空格18            return str.replace(/(^\s*)|(\s*$)/g, "");19     }



(一)使用Annotaion JSR-303标准的验证

首先是要至少导入下面两个包

1、hibernate-validator-4.1.0.Final.jar
2、validation-api-1.0.0.GA.jar


使用这个需要导入支持JSR-303标准的包,建议使用Hibernate Validator这个包,先看这个标准的原生标注

1. Bean Validation 中内置的 constraint

           注解                                      作用

@Valid被注释的元素是一个对象,需要检查此对象的所有字段值@Null被注释的元素必须为 null@NotNull被注释的元素必须不为 null@AssertTrue被注释的元素必须为 true@AssertFalse被注释的元素必须为 false@Min(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值@Max(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值@DecimalMin(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值@DecimalMax(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值@Size(max, min)被注释的元素的大小必须在指定的范围内@Digits (integer, fraction)被注释的元素必须是一个数字,其值必须在可接受的范围内@Past被注释的元素必须是一个过去的日期@Future被注释的元素必须是一个将来的日期@Pattern(value)被注释的元素必须符合指定的正则表达式

2. Hibernate Validator 附加的 constraint

注解作用@Email被注释的元素必须是电子邮箱地址@Length(min=, max=)被注释的字符串的大小必须在指定的范围内@NotEmpty被注释的字符串的必须非空@Range(min=, max=)被注释的元素必须在合适的范围内@NotBlank被注释的字符串的必须非空@URL(protocol=,
host=,    port=, 
regexp=, flags=)被注释的字符串必须是一个有效的url
@CreditCardNumber
被注释的字符串必须通过Luhn校验算法,
银行卡,信用卡等号码一般都用Luhn
计算合法性@ScriptAssert
(lang=, script=, alias=)要有Java Scripting API 即JSR 223 
("Scripting for the JavaTM Platform")的实现@SafeHtml
(whitelistType=, 
additionalTags=)classpath中要有jsoup包
hibernate补充的注解中,最后3个不常用,可忽略。

主要区分下@NotNull  @NotEmpty  @NotBlank 3个注解的区别:

@NotNull           任何对象的value不能为null,用在基本类型上

@NotEmpty       集合对象的元素不为0,即集合不为空,也可以用于字符串不为null,用在集合类上面

@NotBlank        只能用于字符串不为null,并且字符串trim()以后length要大于0,用在String上面



?要使用很简单,在需要验证的变量前面加上该Annotation即可,看下面使用后的User

1
2
3
4
5
6
7
8
9
<codeclass="hljs java">publicclassUser {
    @NotEmpty(message = "用户名不能为空")
    privateString username;
    @Size(min=6,max=20,message = "密码长度不符合标准")
    privateString password;
    privateString nickname;
 
   ......
}</code>

然后再控制器里面加入验证就可以了

@Controller
public class StudentController {
   @RequestMapping(value = "/addStudent", method = RequestMethod.GET)
   public ModelAndView student() {
      return new ModelAndView("addStudent", "command", new Student());
   }

/**

如果没有指定该属性,则默认从request域对象中读取command的表单bean,即默认的modelAttribute 是command
如果该属性值也不存在,则会发生错误
要想显示某个页面,必须在域对象里有一个bean,bean有跟表单path所对应的属性
*/

   @ModelAttribute("student")
   public Student createStudentModel() {    
      return new Student();
   }

   @RequestMapping(value = "/addStudent", method = RequestMethod.POST)
   public String addStudent( @Validated Student student, 
      BindingResult bindingResult, Model model) {
      if (bindingResult.hasErrors()) {
         return "addStudent";
      }

      model.addAttribute("name", student.getName());
      model.addAttribute("age", student.getAge());
      model.addAttribute("id", student.getId());
      return "result";
   }
}

?

配置HibernateValidator-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:mvc="http://www.springframework.org/schema/mvc"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
   http://www.springframework.org/schema/beans     
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/context 
   http://www.springframework.org/schema/context/spring-context-3.0.xsd
   http://www.springframework.org/schema/mvc
   http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">


   <context:component-scan base-package="com.yiibai.springmvc" />
   <mvc:annotation-driven />
<!--动态加载propertites文件内容-->
   <bean class="org.springframework.context.support.ResourceBundleMessageSource"
      id="messageSource">
<!-- 指定资源文件基名称 jdbc为文件名,不包含扩展名  其中Value的值 为properties文件的文件名,不包括扩展名
如果使用property属性,就需要创建propertity文件-->
<property name="basename" value="messages"/>
   </bean>
   <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
      <property name="prefix" value="/WEB-INF/jsp/" />
      <property name="suffix" value=".jsp" />      
   </bean>
</beans>

编写jsp表单
<body>
<h2>学生信息</h2>
<form:form method="POST" action="addStudent"commandName="student">
<table>
<tr>
<td><form:label path="name">姓名:</form:label></td>
<td><form:input path="name" /></td>
<td><form:errors path="name" cssClass="error" /></td>
</tr>
<tr>
<td><form:label path="age">年龄:</form:label></td>
<td><form:input path="age" /></td>
<td><form:errors path="age" cssClass="error" /></td>
</tr>
<tr>
<td><form:label path="id">编号:</form:label></td>
<td><form:input path="id" /></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="提交" /></td>
</tr>
</table>
</form:form>
</body>


然后jsp页面还是之前的页面,验证效果如下,这种方法明显简单多了

一.基于Validator接口的验证.

首先创建User实例,并加入几个属性

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<codeclass="hljs cs">publicclassUser {
    privateString username;
    privateString password;
    privateString nickname;
 
    publicString getUsername() {
        returnusername;
    }
 
    publicvoidsetUsername(String username) {
        this.username = username;
    }
 
    publicString getPassword() {
        returnpassword;
    }
 
    publicvoidsetPassword(String password) {
        this.password = password;
    }
 
    publicString getNickname() {
        returnnickname;
    }
 
    publicvoidsetNickname(String nickname) {
        this.nickname = nickname;
    }
 
    @Override
    publicString toString() {
        return"username--"+username+"password--"+password+"nickname--"+nickname;
    }
}</code>

接着创建用于校检的类UserValidator,让其实现Validator,覆盖其中的两个方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<codeclass="hljs java">importmain.java.model.User;
importorg.springframework.validation.Errors;
importorg.springframework.validation.Validator;
 
publicclassUserValidatorimplementsValidator {
 
 
    @Override
    publicbooleansupports(Class aClass) {
        //判断是否是要校验的类,这里是User
        returnUser.class.equals(aClass);
    }
 
    @Override
    publicvoidvalidate(Object o, Errors errors) {
        User u = (User) o;
        if(null== u.getPassword() || "".equals(u.getPassword())){
            //此方法可以加四个参数,第一个表单域field,
            //区分是哪个表单出错,第二个errorCode错误码,
            //第三个制定了资源文件中占位符,第四个具体错误返回信息
            //简写版可以把2,3参数去掉
            errors.rejectValue("password",null,null,"password is null");
        }
    }
}</code>

上面的类只实现了对密码判断是否为空,为空则注册这一错误信息,也就是”password is null”,接下来要实现控制器,控制器要做的事情,第一是注册这个校验器,第二是实现校验.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<codeclass="hljs java">importmain.java.model.User;
......
 
/**
 * 加上@Controller决定这个类是一个控制器
 */
@Controller
@RequestMapping("/user")
publicclassHelloController {
 
    //我们知道在Controller类中通过@InitBinder标记的方法只有在请求当前Controller的时候才会被执行
    //所以在这里注册校验器
    @InitBinder
    publicvoidinitBainder(DataBinder binder){
        binder.replaceValidators(newUserValidator());
 
    }
    //这个方法主要是跳转到登录页面
    @RequestMapping(value = "/login",method = RequestMethod.GET)
    publicString login(Model model){
        model.addAttribute(newUser());
        return"user/login";
    }
    //处理登录表单
    @RequestMapping(value = "/login",method = RequestMethod.POST)
    publicString login(@ValidatedUser user, BindingResult br){
 
        if(br.hasErrors()){
            return"user/login";
        }
        return"--";
    }
    }</code>

上面代码可以看到@Validated User user, BindingResult br这两个参数,@Validated表明参数user是要校验的类,BindingResult是存储错误信息的类,两者必须一一对应,并且位置挨着,不能中间有其他参数,
最后随便写一个jsp页面实现校检

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<codeclass="hljs xml"><%@ contenttype="text/html;charset=UTF-8"language="java"page="">
<%@taglibprefix="sf"uri="http://www.springframework.org/tags/form">
<%>
    <sf:form method="post"modelattribute="user">
        用户名:<sf:input path="username"></sf:input><sf:errors path="username"></sf:errors>
         
 
        密码:<sf:input path="password"></sf:input><sf:errors path="password"></sf:errors>
         
 
        昵称:<sf:input path="nickname"></sf:input><sf:errors path="nickname"></sf:errors>
         
 
        <input type="submit"value="提交">
    </sf:form></code>
<%@ contenttype="text/html;charset=UTF-8" language="java" page=""><%@taglib prefix="sf" uri="http://www.springframework.org/tags/form"><%>


前面实现的是局部校验,只对当前控制器有效,如果要实现全局校验的话需要配置springMVC.xml文件

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<codeclass="hljs xml">
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemalocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context-3.0.xsd
     http://www.springframework.org/schema/mvc
     http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
 
    <mvc:annotation-driven validator="userValidator"></mvc:annotation-driven>
 
    <beanclass="com.xxx.xxx.UserValidator"id="userValidator"></bean>
 
    ...
</beans></code>


3.定义自己的Annotation Validator

这部分直接从大牛那拷贝过来的.

除了JSR-303原生支持的限制类型之外我们还可以定义自己的限制类型。定义自己的限制类型首先我们得定义一个该种限制类型的注解,而且该注解需要使用@Constraint标注。现在假设我们需要定义一个表示金额的限制类型,那么我们可以这样定义:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<codeclass="hljs avrasm">
importjava.lang.annotation.ElementType;
importjava.lang.annotation.Retention;
importjava.lang.annotation.RetentionPolicy;
importjava.lang.annotation.Target;
 
importjavax.validation.Constraint;
importjavax.validation.Payload;
 
importcom.xxx.xxx.constraint.impl.MoneyValidator;
 
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy=MoneyValidator.class)
public@interfaceMoney {
 
    String message()default"不是金额形式";
 
    Class[] groups()default{};
 
    Class[] payload()default{};
 
}</code>

我们可以看到在上面代码中我们定义了一个Money注解,而且该注解上标注了@Constraint注解,使用@Constraint注解标注表明我们定义了一个用于限制的注解。@Constraint注解的validatedBy属性用于指定我们定义的当前限制类型需要被哪个ConstraintValidator进行校验。在上面代码中我们指定了Money限制类型的校验类是MoneyValidator。另外需要注意的是我们在定义自己的限制类型的注解时有三个属性是必须定义的,如上面代码所示的message、groups和payload属性。
在定义了限制类型Money之后,接下来就是定义我们的限制类型校验类MoneyValidator了。限制类型校验类必须实现接口javax.validation.ConstraintValidator,并实现它的initialize和isValid方法。我们先来看一下MoneyValidator的代码示例:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<codeclass="hljs axapta">
importjava.util.regex.Pattern;
 
importjavax.validation.ConstraintValidator;
importjavax.validation.ConstraintValidatorContext;
 
importcom.xxx.xxx.constraint.Money;
 
publicclassMoneyValidatorimplementsConstraintValidator {
 
    privateString moneyReg = "^\\d+(\\.\\d{1,2})?$";//表示金额的正则表达式
    privatePattern moneyPattern = Pattern.compile(moneyReg);
 
    publicvoidinitialize(Money money) {
       // TODO Auto-generated method stub
 
    }
 
    publicbooleanisValid(Double value, ConstraintValidatorContext arg1) {
       // TODO Auto-generated method stub
       if(value == null)
           returntrue;
       returnmoneyPattern.matcher(value.toString()).matches();
    }
 
}</code>

从上面代码中我们可以看到ConstraintValidator是使用了泛型的。它一共需要指定两种类型,第一个类型是对应的initialize方法的参数类型,第二个类型是对应的isValid方法的第一个参数类型。从上面的两个方法我们可以看出isValid方法是用于进行校验的,有时候我们在校验的过程中是需要取当前的限制类型的属性来进行校验的,比如我们在对@Min限制类型进行校验的时候我们是需要通过其value属性获取到当前校验类型定义的最小值的,我们可以看到isValid方法无法获取到当前的限制类型Money。这个时候initialize方法的作用就出来了。我们知道initialize方法是可以获取到当前的限制类型的,所以当我们在校验某种限制类型时需要获取当前限制类型的某种属性的时候,我们可以给当前的ConstraintValidator定义对应的属性,然后在initialize方法中给该属性赋值,接下来我们就可以在isValid方法中使用其对应的属性了。针对于这种情况我们来看一个代码示例,现在假设我要定义自己的@Min限制类型和对应的MinValidator校验器,那么我可以如下定义:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
<codeclass="hljs handlebars">@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy=MinValidator.class)
public@interfaceMin {
 
    intvalue()default0;
 
    String message();
 
    Class[] groups()default{};
 
    Class[] payload()default{};
}</code>

MinValidator校验器

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<codeclass="hljs axapta">publicclassMinValidatorimplementsConstraintValidator {
 
    privateintminValue;
 
    publicvoidinitialize(Min min) {
       // TODO Auto-generated method stub
       //把Min限制类型的属性value赋值给当前ConstraintValidator的成员变量minValue
       minValue = min.value();
    }
 
    publicbooleanisValid(Integer value, ConstraintValidatorContext arg1) {
       // TODO Auto-generated method stub
       //在这里我们就可以通过当前ConstraintValidator的成员变量minValue访问到当前限制类型Min的value属性了
       returnvalue >= minValue;
    }
 
}</code>

继续来说一下ConstraintValidator泛型的第二个类型,我们已经知道它的第二个类型是对应的isValid的方法的第一个参数,从我给的参数名称value来看也可以知道isValid方法的第一个参数正是对应的当前需要校验的数据的值,而它的类型也正是对应的我们需要校验的数据的数据类型。这两者的数据类型必须保持一致,否则Spring会提示找不到对应数据类型的ConstraintValidator。建立了自己的限制类型及其对应的ConstraintValidator后,其用法跟标准的JSR-303限制类型是一样的。以下就是使用了上述自己定义的JSR-303限制类型——Money限制和Min限制的一个实体类:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<codeclass="hljs cs">publicclassUser {
 
    privateintage;
 
    privateDouble salary;
 
    @Min(value=8, message="年龄不能小于8岁")
    publicintgetAge() {
       returnage;
    }
 
    publicvoidsetAge(intage) {
       this.age = age;
    }
 
    @Money(message="标准的金额形式为xxx.xx")
    publicDouble getSalary() {
       returnsalary;
    }
 
    publicvoidsetSalary(Double salary) {
       this.salary = salary;
    }
 
}</code>