zoukankan      html  css  js  c++  java
  • SpringMVC入门学习(十五)----数据校验

    在Web应用MVC三层架构体系中,表述层负责接收浏览器提交的数据,业务逻辑层负责数据的处理。为了能够让业务逻辑层基于正确的数据进行处理,我们需要在表述层对数据进行检查,将错误的数据隔绝在业务逻辑层之外。在实际的项目中,一般会有两种校验数据的方式:客户端校验和服务端校验

    • 客户端校验:这种校验一般是在前端页面使用JS代码进行校验,主要是验证输入数据的合法性,不合法的数据则没有必要再发送至服务端了,前端校验可以有效的提高用户体验,但是无法确保数据完整性,因为前端用户可以方便的拿到请求地址,然后直接发送请求,传递非法参数。
    • 服务端校验:可以有效的保证数据安全与完整性,但是用户体验要差一点,所以客户端校验和服务端校验通常两者结合使用。

    本文是服务端的校验。Spring MVC提供了多种校验机制,其中有Bean Validation及Spring Validator接口校验。在Spring 4.0之后,支持Bean Validation 1.0(JRS-303)和Bean Validation 1.1(JRS-349)校验,可以单独集成Hibernate的validation校验框架,用于服务端的数据校验。

    1、校验概述

    JSR 303是Java为Bean数据合法性校验提供的标准框架,它已经包含在JavaEE 6.0标准中。JSR 303通过在Bean 属性上标注类似于@NotNull、@Max等标准的注解指定校验规则,并通过标准的验证接口对Bean进行验证。

    注解 作用
    @Null 标注的属性必须为null
    @NotNull 标注的属性必须不为null
    @AssertTrue 标注的属性必须为true
    @AssertFalse 标注的属性必须为false
    @Min(value) 标注的属性必须是一个数字,并且其值必须大于或等于value
    @Max(value) 标注的属性必须是一个数字,并且其值必须小于或等于value
    @DecimalMin(value) 必须大于或等于value
    @DecimalMax(value) 必须小于或等于value
    @Size(max,min) 大小必须在max和min限定的范围内
    @Digits(integer,fratction) 值必须是一个数字,且必须在可接受的范围内
    @Past 只能用于日期型,且必须是过去的日期
    @Future 只能用于日期型,且必须是将来的日期
    @Pattern(value) 必须符合指定的正则表达式

    JSR 303只是一套标准,需要提供其实现才可以使用。而Hibernate Validator是JSR 303的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解:

    注解 作用
    @Email 必须是格式正确的Email地址
    @Length 被注释的字符串大小必须在指定的范围内
    @NotEmpty 被注释的字符串不能是空字符串
    @Range 被注释的元素必须在指定的范围内

    特别注意:@NotEmpty、@NotNull和@NotBlank三种的区别:

    • @NotNull:一般用在基本数据类型上(包括包装类),对象不能为null,但可以为empty,即为空集(size = 0)。
    • @NotEmpty:可以作用在String、List、Map和Array等,对象不能为null,而且长度必须大于0 (size > 0)
    • @NotBlank:只能作用在String上,不能为null,而且调用trim()后,长度必须大于0 ,即:必须有实际字符

    2、普通校验

    [1]、导入校验相关的依赖

    <dependency>
        <groupId>org.hibernate.validator</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>6.2.0.Final</version>
    </dependency>
    

    [2]、在springmvc.xml配置文件中配置校验器

        <!-- 配置校验器 -->
        <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
            <!-- 校验器-->
            <property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
            <!-- 指定校验使用的资源文件,如果不指定则默认使用classpath下的ValidationMessages.properties -->
            <property name="validationMessageSource" ref="messageSource"/>
        </bean>
        <!-- 校验错误信息配置文件 -->
        <bean id="messageSource"
              class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
            <!-- 资源文件名-->
            <property name="basenames">
                <list>
                    <value>classpath:CustomValidationMessages</value>
                </list>
            </property>
            <!-- 资源文件编码格式 -->
            <property name="defaultEncoding" value="utf-8"/>
            <!-- 对资源文件内容缓存时间,单位秒 -->
            <property name="cacheSeconds" value="120"/>
        </bean>
    

    [3]、将配置的校验器注入到处理器适配器中

    <!-- 配置MVC注解驱动,配置注入校验器 -->
    <mvc:annotation-driven validator="validator"/>
    

    [4]、创建校验错误的信息,在项目中创建一个名称为CustomValidationMessages.properties 的文件(因为上面的配置文件中叫这个名字):

    #添加校验错误提示信息
    user.id.isEmpty="用户的ID不能为空!"
    user.userName.isEmpty="用户名不能为空!"
    user.userName.length="用户名为1~6个字符!"
    user.userPwd.isEmpty="密码不能为空!"
    user.userPwd.length="密码的长度为5~15个字符!"
    user.userEmail.isEmpty="邮箱不能为空!"
    user.userEmail.format="输入的邮箱格式不正确!"
    

    [5]、在pojo实体类中添加校验规则

    public class User {
    
        @NotNull(message = "{user.id.isEmpty}")
        private Integer id;
    
        @NotEmpty(message = "{user.userName.isEmpty}")
        @Length(min = 1, max = 6, message = "{user.userName.length}")
        private String userName;
    
        @NotEmpty(message = "{user.userPwd.isEmpty}")
        @Length(min = 5, max = 15, message = "{user.userPwd.length}")
        private String userPwd;
    
        @NotEmpty(message = "{user.userEmail.isEmpty}")
        @Email(message = "{user.userEmail.format}")
        private String userEmail;
    
        // getter setter 构造器 toString省略...
    }
    

    [6]、捕获校验错误信息Controller代码

    注意:@Validated注解和BindingResult是配对出现的,中间不能穿插其它的形参,否则会报400错误,你要进入其它形参可以在它两的后面写。

    /**
     * 数据校验controller
     */
    @Controller
    public class ValidateController {
    
        @ResponseBody
        @RequestMapping("/validate")
        // 形参前面加上@Validated注解表示这个实体类需要进行数据校验
        // BindingResult 封装数据绑定的结果
        public void validate(@Validated User user, BindingResult bindingResult) {
            if (bindingResult.hasErrors()) {
                //校验未通过,获取所有的异常信息并展示出来
                List<ObjectError> allErrors = bindingResult.getAllErrors();
                for (ObjectError allError : allErrors) {
                    System.out.println(allError.getObjectName() + ":" + allError.getDefaultMessage());
                }
            }
        }
    }
    

    [7]、测试结果如下所示:

    ①、页面上什么也不输入

    image image

    ②、输入部分错误格式的内容(密码长度小于5,邮箱格式错误!)

    image image

    3、分组校验

    在进行校验的时候,校验的规则一般都是写在实体类上面的,而有时一个实体类会被多处使用,例如不同的Controller中需要使用同一个实体,当不同的Controller方法对同一个实体对象进行校验时,每个Controller方法需要不同的校验,所以对于这种情况,就需要使用分组校验。

    [1]、首先定义校验组,所谓的校验组,它其实就是空接口:

    注意:分组接口中不需要编写任何的方法定义,该接口仅仅作为分组校验的一个标识接口。

    // 分组检验接口1
    public interface ValidationGroup1 {
    }
    
    // 分组检验接口2
    public interface ValidationGroup2 {
    }
    

    [2]、在实体类中为每一个校验的规则设置所属组:

    public class User {
    
        // groups属性表示校验属于哪个组,可以定义多个
        @NotNull(message = "{user.id.isEmpty}", groups = {ValidationGroup2.class})
        private Integer id;
    
        @NotEmpty(message = "{user.userName.isEmpty}", groups = {ValidationGroup1.class, ValidationGroup2.class})
        @Length(min = 1, max = 6, message = "{user.userName.length}", groups = {ValidationGroup1.class, ValidationGroup2.class})
        private String userName;
    
        @NotEmpty(message = "{user.userPwd.isEmpty}", groups = {ValidationGroup1.class})
        @Length(min = 5, max = 15, message = "{user.userPwd.length}", groups = {ValidationGroup1.class})
        private String userPwd;
    
        @NotEmpty(message = "{user.userEmail.isEmpty}", groups = {ValidationGroup2.class})
        @Email(message = "{user.userEmail.format}", groups = {ValidationGroup2.class})
        private String userEmail;
    
        // getter setter 构造器 toString省略...
    }
    

    [3]、然后在接收参数的地方,指定校验组(配置完成后,属于 ValidationGroup2 这个组的校验规则,才会生效。):

    /**
     * 数据校验controller
     */
    @Controller
    public class ValidateController {
    
        @ResponseBody
        @RequestMapping("/validate")
        // @Validated注解value参数为:ValidationGroup2.class,表示只有这个组内的校验规则会生效,其它都不会生效
        public void validate(@Validated(value = {ValidationGroup2.class}) User user, BindingResult bindingResult) {
            if (bindingResult.hasErrors()) {
                //校验未通过,获取所有的异常信息并展示出来
                List<ObjectError> allErrors = bindingResult.getAllErrors();
                for (ObjectError allError : allErrors) {
                    System.out.println(allError.getObjectName() + ":" + allError.getDefaultMessage());
                }
            }
        }
    }
    

    [4]、最后测试一下效果有没有:

    前面指定的校验组为ValidationGroup2组,而实体中只有userPwd属性只属于ValidationGroup1组,所以可以看到控制台打印的信息中,关于密码的校验信息是没有打印的,说明是校验的ValidationGroup2组。

    image image

    作者: 唐浩荣
    本文版权归作者和博客园共有,欢迎转载,但是转载需在博客的合适位置给出原文链接,否则保留追究法律责任的权利。
  • 相关阅读:
    锁优化
    无同步方案
    线程安全
    vue 监听路由变化
    ES6-解构赋值
    微信小程序中遮罩层的滚动穿透问题
    JavaScirpt对象原生方法
    JavaScript-window
    JavaScript字符串API
    JavaScript常用数组操作方法,包含ES6方法
  • 原文地址:https://www.cnblogs.com/tanghaorong/p/14792271.html
Copyright © 2011-2022 走看看