精华内容
下载资源
问答
  • 主要介绍了Spring Validation实现原理分析,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 主要介绍了如何使用Spring Validation优雅地校验参数,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
  • spring-validation-源码

    2021-03-10 19:58:43
    Spring验证 我正在使用该项目作为有关如何改进传入数据的Spring验证的演示/参考项目。 这个想法是要帮助我摆脱原始痴迷,并改善如何验证数据的方式。 进行改进 确保所有内容都有GET / POST / PUT / DELETE调用。 ...
  • 强悍的Springspring validation

    万次阅读 多人点赞 2019-02-16 15:33:31
    spring validation实现对Restful请求的数据进行校验 1、前言 数据的校验是交互式网站一个不可或缺的功能,前端的js校验可以涵盖大部分的校验职责,如用户名唯一性,生日格式,邮箱格式校验等等常用的校验。但是...

    spring validation实现对Restful请求的数据进行校验


    1、前言

    数据的校验是交互式网站一个不可或缺的功能,前端的js校验可以涵盖大部分的校验职责,如用户名唯一性,生日格式,邮箱格式校验等等常用的校验。但是为了避免用户绕过浏览器,使用http工具直接向后端请求一些违法数据,服务端的数据校验也是必要的,可以防止脏数据落到数据库中,如果数据库中出现一个非法的邮箱格式,也会让运维人员头疼不已。可以使用本文将要介绍的validation来对数据进行校验。

    2、常用校验

    1. JSR303/JSR-349: JSR303是一项标准,只提供规范不提供实现,规定一些校验规范即校验注解,如@Null,@NotNull,@Pattern,位于javax.validation.constraints包下。JSR-349是其的升级版本,添加了一些新特性。

      • @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 validation:hibernate validation是对这个规范的实现,并增加了一些其他校验注解,如@Email,@Length,@Range等等

      • @Email 被注释的元素必须是电子邮箱地址
      • @Length 被注释的字符串的大小必须在指定的范围内
      • @NotEmpty 被注释的字符串的必须非空
      • @Range 被注释的元素必须在合适的范围内
    3. spring validation:spring validation对hibernate validation进行了二次封装,在springmvc模块中添加了自动校验,并将校验信息封装进了特定的类中

    3、spring boot的数据自动校验功能

    3.1 引入依赖

    spring-web模块使用了hibernate-validation,并且databind模块也提供了相应的数据绑定功能。

    <dependencies>
        <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
    

    我们只需要引入spring-boot-starter-web依赖即可,如果查看其子依赖,可以发现如下的依赖:

    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
    

    3.2 构建启动类

    @SpringBootApplication
        public class App {
        public static void main(String[] args) {
            SpringApplication.run(App.class, args);
            System.out.println("Start app success.");
        }
    }
    

    3.3 创建需要被校验的实体类

    public class Person {
    @NotEmpty(message = "name不能为空")
    private String name;
    @Range(min = 0, max = 100, message = "age不能大于100小于0")
    private int age;
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }
    

    3.4 在Controller中校验数据

    springmvc为我们提供了自动封装表单参数的功能,一个添加了参数校验的典型controller如下所示。

    @RequestMapping("/test")
    public String valid(@Validated Person person, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            for (FieldError fieldError : bindingResult.getFieldErrors()) {
                System.out.println(fieldError);
            }
            return "fail";
        }
        return "success";
    }
    

    值得注意的地方:

    1. 参数Persion前需要加上@Validated注解,表明需要spring对其进行校验,而校验的信息会存放到其后的BindingResult中。注意,必须相邻,如果有多个参数需要校验,形式可以如下。valid(@Validated Person person, BindingResult fooBindingResult ,@Validated Bar bar, BindingResult barBindingResult);即一个校验类对应一个校验结果。
    2. 校验结果会被自动填充,在controller中可以根据业务逻辑来决定具体的操作,如跳转到错误页面。 一个最基本的校验就完成了.

    启动容器测试结果如下:

    Field error in object 'person' on field 'age': rejected value [105]; codes [Range.person.age,Range.age,Range.int,Range]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [person.age,age]; arguments []; default message [age],100,0]; default message [age不能大于100小于0]
    

    3.5 统一异常处理

    前面那种方式处理校验错误,略显复杂,而且一般网站都会对请求错误做统一的404页面封装,如果数据校验不通过,则Spring boot会抛出BindException异常,我们可以捕获这个异常并使用Result封装返回结果。通过@RestControllerAdvice定义异常捕获类。

    Controller类:

    @RequestMapping(value = "valid", method = RequestMethod.GET)
    public String valid(@Validated Person person) {
        System.out.println(person);
        return "success";
    }
    

    统一异常处理类:

    @RestControllerAdvice
    public class BindExceptionHanlder {
        @ExceptionHandler(BindException.class)
        public String handleBindException(HttpServletRequest request, BindException exception) {
            List<FieldError> allErrors = exception.getFieldErrors();
            StringBuilder sb = new StringBuilder();
            for (FieldError errorMessage : allErrors) {
                sb.append(errorMessage.getField()).append(": ").append(errorMessage.getDefaultMessage()).append(", ");
            }
            System.out.println(sb.toString());
            return sb.toString();
        }
    }
    

    测试: http://localhost:8080/valid?age=105&name=steven

    输出:age: age不能大于100小于0,

    4、自定义校验注解

    4.1 @NameValidation

    @Documented
    @Constraint(validatedBy = NameValidationValidator.class)
    @Target({ElementType.METHOD, ElementType.FIELD})
    @Retention(RUNTIME)
    public @interface NameValidation {
        String message() default "不是合法的名字";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    
        @Target({PARAMETER, ANNOTATION_TYPE})
        @Retention(RUNTIME)
        @Documented
        @interface List {
            NameValidation[] value();
        }
    }
    

    4.2 校验类NameValidationValidator

    public class NameValidationValidator implements ConstraintValidator<NameValidation, String> {
        @Override
        public boolean isValid(String value, ConstraintValidatorContext context) {
            if ("steven".equalsIgnoreCase(value)) {
                return true;
            }
            String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate();
            System.out.println("default message :" + defaultConstraintMessageTemplate);
            //禁用默认提示信息
            //context.disableDefaultConstraintViolation();
            //设置提示语
            //context.buildConstraintViolationWithTemplate("can not contains blank").addConstraintViolation();
            return false;
        }
    }
    

    4.3 在Person类增加新注解

    @NotEmpty(message = "name不能为空")
    @NameValidation
    private String name;
    

    测试: http://localhost:8080/valid?age=105&name=lxy

    输出:age: age不能大于100小于0, name: 不是合法的名字,

    展开全文
  • Spring 整合 Spring Validation 自动验证

    千次阅读 2019-06-27 17:42:21
    Spring Validation JSR-303 简介 JSR-303 是 JavaEE 6 中的一项子规范,叫做 Bean Validation,官方参考实现是 Hibernate Validator。 此实现与 Hibernate ORM 没有任何关系。JSR-303 用于对 Java Bean 中的字段的值...

    Spring Validation

    JSR-303 简介

    JSR-303 是 JavaEE 6 中的一项子规范,叫做 Bean Validation,官方参考实现是 Hibernate Validator。

    此实现与 Hibernate ORM 没有任何关系。JSR-303 用于对 Java Bean 中的字段的值进行验证。 Spring MVC 3.x 之中也大力支持 JSR-303,可以在控制器中使用注解的方式对表单提交的数据方便地验证。

    Spring 4.0 开始支持 Bean Validation 功能。

    JSR-303 基本的校验规则

    空检查

    • @Null 验证对象是否为 null
    • @NotNull 验证对象是否不为 null, 无法查检长度为 0 的字符串
    • @NotBlank 检查约束字符串是不是 Null 还有被 Trim 的长度是否大于 0,只对字符串,且会去掉前后空格
    • @NotEmpty 检查约束元素是否为 NULL 或者是 EMPTY

    布尔检查

    • @AssertTrue 验证 Boolean 对象是否为 true
    • @AssertFalse 验证 Boolean 对象是否为 false

    长度检查

    • @Size(min=, max=) 验证对象(Array, Collection , Map, String)长度是否在给定的范围之内
    • @Length(min=, max=) 验证字符串长度介于 min 和 max 之间

    日期检查

    • @Past 验证 Date 和 Calendar 对象是否在当前时间之前,验证成立的话被注释的元素一定是一个过去的日期
    • @Future 验证 Date 和 Calendar 对象是否在当前时间之后 ,验证成立的话被注释的元素一定是一个将来的日期

    正则检查

    • @Pattern 验证 String 对象是否符合正则表达式的规则,被注释的元素符合制定的正则表达式
      regexp:正则表达式
      flags:指定 Pattern.Flag 的数组,表示正则表达式的相关选项

    数值检查

    注意: 建议使用在 String ,Integer 类型,不建议使用在 int 类型上,因为表单值为 “” 时无法转换为 int,但可以转换为 String 为 “”,Integer 为 null

    • @Min 验证 Number 和 String 对象是否大等于指定的值
    • @Max 验证 Number 和 String 对象是否小等于指定的值
    • @DecimalMax 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过 BigDecimal 定义的最大值的字符串表示 .小数 存在精度
    • @DecimalMin 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过 BigDecimal 定义的最小值的字符串表示 .小数 存在精度
    • @Digits 验证 Number 和 String 的构成是否合法
    • @Digits(integer=,fraction=) 验证字符串是否是符合指定格式的数字,integer 指定整数精度,fraction 指定小数精度
    • @Range(min=, max=) 被指定的元素必须在合适的范围内
    • @Valid 递归的对关联对象进行校验, 如果关联对象是个集合或者数组,那么对其中的元素进行递归校验,如果是一个 map,则对其中的值部分进行校验.(是否进行递归验证)
    • @CreditCardNumber 信用卡验证
    • @Email 验证是否是邮件地址,如果为 null,不进行验证,算通过验证
    • @ScriptAssert(lang= ,script=, alias=)
    • @URL(protocol=,host=, port=,regexp=, flags=)

    使用 Spring Validation 验证

    POM

    这里我们使用 Hibernate Validator 5.x 来实现 Spring Validation 接口,pom.xml 文件如下:

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

    主要是增加了 org.hibernate:hibernate-validator 依赖

    定义验证工具类BeanValidator

    已为大家封装好了工具类,享用即可。创建一个名为 BeanValidator 的工具类,代码如下:

    package com.funtl.my.shop.commons.validator;
    
    import org.springframework.beans.factory.annotation.Autowired;
    
    import javax.validation.ConstraintViolation;
    import javax.validation.ConstraintViolationException;
    import javax.validation.Validator;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    /**
     * JSR303 Validator(Hibernate Validator)工具类.
     * <p>
     * ConstraintViolation 中包含 propertyPath, message 和 invalidValue 等信息.
     * 提供了各种 convert 方法,适合不同的 i18n 需求:
     * 1. List<String>, String 内容为 message
     * 2. List<String>, String 内容为 propertyPath + separator + message
     * 3. Map<propertyPath, message>
     * <p>
     * 详情见wiki: https://github.com/springside/springside4/wiki/HibernateValidator
     *
     * <p>Title: BeanValidator</p>
     * <p>Description: </p>
     *
     * @author Lusifer
     * @version 1.0.0
     * @date 2018/6/26 17:21
     */
    public class BeanValidator {
    	//Spring无法@Autoware static成员变量,所以在xml中set注入
        private static Validator validator;
    
        public static void setValidator(Validator validator) {
            BeanValidator.validator = validator;
        }
    
        /**
         * 调用 JSR303 的 validate 方法, 验证失败时抛出 ConstraintViolationException.
         */
        private static void validateWithException(Validator validator, Object object, Class<?>... groups) throws ConstraintViolationException {
            Set constraintViolations = validator.validate(object, groups);
            if (!constraintViolations.isEmpty()) {
                throw new ConstraintViolationException(constraintViolations);
            }
        }
    
        /**
         * 辅助方法, 转换 ConstraintViolationException 中的 Set<ConstraintViolations> 中为 List<message>.
         */
        private static List<String> extractMessage(ConstraintViolationException e) {
            return extractMessage(e.getConstraintViolations());
        }
    
        /**
         * 辅助方法, 转换 Set<ConstraintViolation> 为 List<message>
         */
        private static List<String> extractMessage(Set<? extends ConstraintViolation> constraintViolations) {
            List<String> errorMessages = new ArrayList<>();
            for (ConstraintViolation violation : constraintViolations) {
                errorMessages.add(violation.getMessage());
            }
            return errorMessages;
        }
    
        /**
         * 辅助方法, 转换 ConstraintViolationException 中的 Set<ConstraintViolations> 为 Map<property, message>.
         */
        private static Map<String, String> extractPropertyAndMessage(ConstraintViolationException e) {
            return extractPropertyAndMessage(e.getConstraintViolations());
        }
    
        /**
         * 辅助方法, 转换 Set<ConstraintViolation> 为 Map<property, message>.
         */
        private static Map<String, String> extractPropertyAndMessage(Set<? extends ConstraintViolation> constraintViolations) {
            Map<String, String> errorMessages = new HashMap<>();
            for (ConstraintViolation violation : constraintViolations) {
                errorMessages.put(violation.getPropertyPath().toString(), violation.getMessage());
            }
            return errorMessages;
        }
    
        /**
         * 辅助方法, 转换 ConstraintViolationException 中的 Set<ConstraintViolations> 为 List<propertyPath message>.
         */
        private static List<String> extractPropertyAndMessageAsList(ConstraintViolationException e) {
            return extractPropertyAndMessageAsList(e.getConstraintViolations(), " ");
        }
    
        /**
         * 辅助方法, 转换 Set<ConstraintViolations> 为 List<propertyPath message>.
         */
        private static List<String> extractPropertyAndMessageAsList(Set<? extends ConstraintViolation> constraintViolations) {
            return extractPropertyAndMessageAsList(constraintViolations, " ");
        }
    
        /**
         * 辅助方法, 转换 ConstraintViolationException 中的 Set<ConstraintViolations> 为 List<propertyPath + separator + message>.
         */
        private static List<String> extractPropertyAndMessageAsList(ConstraintViolationException e, String separator) {
            return extractPropertyAndMessageAsList(e.getConstraintViolations(), separator);
        }
    
        /**
         * 辅助方法, 转换 Set<ConstraintViolation> 为 List<propertyPath + separator + message>.
         */
        private static List<String> extractPropertyAndMessageAsList(Set<? extends ConstraintViolation> constraintViolations, String separator) {
            List<String> errorMessages = new ArrayList<>();
            for (ConstraintViolation violation : constraintViolations) {
                errorMessages.add(violation.getPropertyPath() + separator + violation.getMessage());
            }
            return errorMessages;
        }
    
        /**
         * 服务端参数有效性验证
         *
         * @param object 验证的实体对象
         * @param groups 验证组
         * @return 验证成功:返回 null;验证失败:返回错误信息
         */
        public static String validator(Object object, Class<?>... groups) {
            try {
                validateWithException(validator, object, groups);
            } catch (ConstraintViolationException ex) {
                List<String> list = extractMessage(ex);
                list.add(0, "数据验证失败:");
    
                // 封装错误消息为字符串
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < list.size(); i++) {
                    String exMsg = list.get(i);
                    if (i != 0 ){
                        sb.append(String.format("%s. %s", i, exMsg)).append(list.size() > 1 ? "<br/>" : "");
                    } else {
                        sb.append(exMsg).append(list.size() > 1 ? "<br/>" : "");
                    }
                }
    
                return sb.toString();
            }
    
            return null;
        }
    }
    

    修改实体类

    修改实体类,增加验证注解,以后我们只需要在实体类的属性上使用 JSR-303 注解即可完成相关数据的验证工作,关键代码如下:

    @Length(min = 6, max = 20, message = "用户名长度必须介于 6 和 20 之间")
    private String username;
    @Length(min = 6, max = 20, message = "密码长度必须介于 6 和 20 之间")
    private String password;
    @Pattern(regexp = RegexpUtils.PHONE, message = "手机号格式不正确")
    private String phone;
    @Pattern(regexp = RegexpUtils.EMAIL, message = "邮箱格式不正确")
    private String email;
    

    注入工具类

    修改 spring-context.xml 文件,注入 Validator工具类,配置如下:

    <!-- 配置 Bean Validator 定义 -->
    <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
    <bean id="beanValidator" class="com.funtl.my.shop.commons.validator.BeanValidator">
        <property name="validator" ref="validator" />
    </bean>
    

    调用BeanValidator工具类验证

    TbUser tbUser = new TbUser();
    tbUser.setName="aa2233";
    tbUser.setEmail="123456@qq.com";
    String validator = BeanValidator.validator(tbUser);
    // 验证不通过
    if (validator != null) {
    	//···  ···
    } 
    // 验证通过
    else {
    	//···  ···
    }
    
    展开全文
  • 本章讲述通过spring validation 实现请求参数验证的常规用法及自定义注解方案

    本章演示代码:https://gitee.com/tysite-web/tysite-service/tree/master/src/main/java/org/tysite/tyservice/example/validation

    说明

    《spring mvc 接收参数注解对比及最佳使用方案推荐》章节中作者已经整理了RESTful接口的传参方案。而在日常的项目开发中,我们需要严格控制参数规范,以避免不合规的参数导致程序处理异常。
    spring validation为我们提供了基于POJO类的参数验证解决方案,本文将详细整理相关知识。

    一. 依赖添加

    在我们的tysite-service搭建时依赖使用的是org.springframework.boot:spring-boot-starter-web,该依赖中已经引用spring-boot-starter-validation,无需额外添加。
    spring validation相关依赖

    二. 常用的约束验证注解

    面对服务器接口参数校验问题,J2EE 6 定义了一项子规范:Bean Validation,官方实现是Hibernate Validator,Spring 4.0 之后的版本均已经支持Bean Validation功能。

    常用的约束验证注解,如下表所示:

    约束注解描述适用场景
    @null被注释元素必须为NULL
    @NotNull被注释元素必须不为NULL,无法检查长度为0的字符串
    @NotEmpty被注释元素不能为 NULL或 EMPTY
    @NotBlank被注释元素不能为 NULL 且 字符串trim后长度必须大于0适用于验证字符串字段非空
    @AssertTrue被注释元素必须为Boolean类型的 true适用于属性必须为true的场景
    @AssertFalse被注释元素必须为Boolean类型的 false适用于属性必须为false的场景
    @Min被注释元素必须大于指定数值,格式为 long 类型适用于属性限制long类型数据最小值的场景
    @Max被注释元素必须小于指定数值,格式为 long 类型适用于属性限制long类型数据最大值的场景
    @DecimalMin被注释元素必须大于指定数值,参数为BigDecimal定义的小数适用于属性限制小数数据最小值的场景
    @DecimalMax被注释元素必须大于指定数值,参数为BigDecimal定义的小数适用于属性限制小数数据最大值的场景
    @Digits被注释元素必须为指定格式的数字,integer指定整数精度,fraction指定小数精度适用于属性限制金额格式,字段类型必须使用 Double
    @Size被注释元素必须的大小必须在指定的范围内适用于属性限制字符串长度的场景
    @Length被注释元素必须的大小必须在指定的范围内
    @Past被注释元素必须是过去的一个日期适用于属性限制必须为过去日期的场景
    @Future被注释元素必须是未来的一个日期适用于属性限制必须为未来日期的场景
    @Pattern被注释元素必须符合正则表达式regexp的规范适用于属性为密码、身份证号等需要正则限制的场景
    @Email被注释元素必须符合Email格式适用于属性限制为Email格式的场景
    @Range被注解的元素(可以是数字或者表示数字的字符串)必须在给定的范围内
    @URL被注释元素必须符合URL格式规范适用于属性限制为网址的场景

    备注:spring validation提供默认的错误提示信息,在使用时也可以通过注解的message自定义错误提示信息。

    三. spring validation 常规用法

    spring validation的使用需要完成两步操作:

    1、以POJO类接收参数,在该POJO类的属性上通过约束验证注解,限定参数格式。

    以下为作者根据个人经验,整理的常用验证规范的使用样例

    ……
        private enum SexEnum {
            /** 男 */
            MALE,
            /** 女 */
            FEMALE
        }
        
        /** 主键ID */
        private Integer id;
        
        /** 用户名 */
        @NotBlank(message = "请输入用户名")
        @Size(min = 3, max = 20, message = "用户名长度必须在3~20之间")
        private String username;
        
        /** 名称 */
        @NotBlank(message = "请输入姓名")
        @Size(min = 2, max = 20, message = "姓名长度必须在2~20之间")
        private String name;
        
        /** 资源名称 */
        @NotBlank(message = "请输入资源名称")
        @Size(max = 50, message = "资源名称长度必须在1~50之间")
        private String resourceName;
        
        /** 文本域 */
        @NotBlank(message = "请输入资源简介")
        @Size(max = 1000, message = "文本域长度必须在1~1200之间")
        private String textField;
        
        /** 密码 */
        @NotBlank(message = "请输入密码")
        @Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[$@$!%*?&])[A-Za-z\\d$@$!%*?&]{8,20}",
            message = "密码长度8~20位,必须包含数字、字母(大小写各一个)、特殊字符")
        private String password;
        
        /** 性别 */
        private SexEnum genders;
        
        /** 邮箱 */
        @Email(message = "请输入正确的邮箱地址")
        private String email;
        
        /** 手机号码 */
        @Pattern(regexp = "^1[3|4|5|8][0-9]\\d{8}$", message = "请输入正确的手机号码")
        private String phone;
        
        /** 存款 */
        @Digits(integer = 14, fraction = 2, message = "请输入有效的金额")
        private Double deposit;
        
        /** 出生日期 */
        @Past(message = "请输入正确的出生日期")
        private Date birthday;
        
        /** 身高 */
        @NotNull(message = "请填写您的身高")
        @DecimalMin(value = "44.7", message = "请填写有效的身高")
        private Float stature;
        
        /** 爱好 */
        @Valid
        private List<AmateurDTO> amateur;
        
        /** 博客 */
        @URL
        private String blogs;
    ……
    

    源代码地址:UserInfoDemoDTO

    2、在 Controller 中,使用添加属性验证注解的POJO类接收参数,并且在POJO类前使用@Validated 注解。

    ……
        @PostMapping("")
        public UserInfoDemoDTO addDemo(@Validated() @RequestBody UserInfoDemoDTO dto) {
            return dto;
        }
    ……
    

    源代码地址:ValidationController

    四. 枚举类型验证

    细心的朋友会看到,作者对 枚举类型的 性别字段,并未使用格式验证注解。
    这是因为spring mvc 中,在spring validation验证调用之前,会优先进行反序列化,如果请求参数不符合枚举类型规范,则会直接抛出HttpMessageConversionException异常。
    反序列化异常

    五. 嵌套注解的应用

    在参数接收的实际业务场景中,我们经常会遇到需要接收对象属性的场景,如下图所示:
    amateur
    在这种业务场景下,仅使用@Validated 注解,是无法验证对象列表中属性值的合法性的。
    这时候我们需要使用@Valid注解的嵌套验证功能。

    首先,我们在作为列表对象的POJO类 AmateurDTO 中设置属性验证规范。

    ……
        @NotNull(message = "兴趣爱好的id不能为空")
        private Integer id;
        
        @NotBlank(message = "兴趣爱好的名称不能为空")
        @Size(max = 50, message = "兴趣爱好的长度必须在1~50之间")
        private String name;
    ……
    

    然后,我们在UserInfoDemoDTO示例类的 List<AmateurDTO>类型属性amateur上,添加@Valid注解,如下所示:

    ……
        /** 爱好 */
        @Valid
        private List<AmateurDTO> amateur;
    ……
    

    最后,我们采用不合规的爱好名称请求演示API,验证提示信息如下:
    在这里插入图片描述

    六. @Validated 分组功能

    @Validated 注解还支持参数验证的分组功能,可以根据分组接口类,判断验证注解是否生效。

    这里作者以 “创建操作,允许ID为空;更新操作,不允许ID为空” 的场景作为演示示例

    首先,我们在org.tysite.tyservice.example.validation.group包下创建两个接口类 AddOperationModifyOperation,用于判断添加和修改操作。

    /** @Validated 分组类 - 新增数据 */
    public interface AddOperation {
    }
    
    /** @Validated 分组类 - 修改数据 */
    public interface ModifyOperation {
    }
    

    然后,我们在POJO类GroupDemoDTOid 属性设置验证分组

    ……
        @NotNull(message = "编辑操作ID不能为空", groups = ModifyOperation.class)
        private Integer id;
    ……
    

    最后,在Controller类ValidationGroupController中,分别传入AddOperation.classModifyOperation.class分组接口

    ……
        @PostMapping("/add")
        public GroupDemoDTO insertDemo(@Validated({AddOperation.class}) @RequestBody GroupDemoDTO dto) {
            return dto;
        }
        
        @PutMapping("/update")
        public GroupDemoDTO modifyDemo(@Validated({ModifyOperation.class}) @RequestBody GroupDemoDTO dto) {
            return dto;
        }
    ……
    

    下面我们分表以添加和修改接口的调用,演示请求结果。
    调用添加接口:
    添加接口
    调用修改接口:
    编辑接口

    七. 自定义校验注解

    上面的 Spring Validation 参数验证方案,基本上可以解决我们日常工作中遇到的参数验证场景,如果有其他特殊的验证需求,我们也可以自定义验证注解来实现。

    这里我们可以模仿validation官方注解的语法,实现自定义注解。

    首先,通过阅读@NotBlank@Email@Digits@Past 等注解的源代码,我们会发现validation的官方注解,都使用了@Constraint(validatedBy = { }) 注解。

    打开该注解的源代码,通过阅读源码注释,我们发现该注解要求必须包含 message()groups()payload() 三个属性。
    注解必须包含的属性
    继续阅读源代码,我们会发现源代码说明中给我们提供了Validation自定义注解的样例代码,如下图所示:
    validation示例代码
    根据以上示例代码中 @Constraint(validatedBy = OrderNumberValidator.class) 的语法规则,我们找到@NotBlank注解的注解解析器NotBlankValidator ,我们会发现该注解实现了ConstraintValidator接口。

    ……
    public class NotBlankValidator implements ConstraintValidator<NotBlank, CharSequence> {
    ……
    

    然后,阅读ConstraintValidator接口的源代码,我们发现该接口包含 initializeisValid 方法。

    • initialize 方法为初始化方法,其参数为constraintAnnotation,我们可以通过该参数获取到注解的相关信息,如注解设置的属性值等。
    • isValid 方法为验证方法,用于判断验证对象是否合规。也就是说我们可以通过实现该方法完成验证逻辑。 其参数包含验证对象value和约束验证器的上下文context

    ConstraintValidator源代码
    注解基本语法可参考作者博客:https://blog.csdn.net/tysite/article/details/90691368

    了解了验证注解和其注解解析器的语法规则,我们就可以根据自己的业务需要,开发自己的验证注解了,如下所示:

    首先,我们编写注解类 @DemoName

    @Target({ ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = {DemoNameValidator.class})
    @Documented
    public @interface DemoName {
        
        String message() default "名称的长度必须在1~20之间";
        
        Class<?>[] groups() default {};
        
        Class<? extends Payload>[] payload() default {};
    }
    

    然后,我们编写 注解解析器类 DemoNameValidator

    public class DemoNameValidator implements ConstraintValidator<DemoName, CharSequence> {
        
        @Override
        public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
            if ( value == null ) {
                return false;
            } else {
                Integer charLength = value.toString().trim().length();
                if (charLength >= 1 && charLength <= Constant.NAME_MAX) {
                    return true;
                } else {
                    return false;
                }
            }
        }
    }
    

    最后,编写验证代码并调用验证入口API ,使用不合规的参数,可以得到如下验证效果。
    自定义验证注解
    详细示例代码,可以通过作者开源项目tysite-serviceorg.tysite.tyservice.example.validation包中找到
    https://gitee.com/tysite-web/tysite-service

    展开全文
  • Spring validation 用法说明

    千次阅读 2019-10-17 15:40:04
    后端开发接口,经常会需要校验接口,可以使用spring提供的validation框架,使用注解进行校验,很方便。 如果提供的注解满足不了业务需求,可以自己定义,也很方便; 本文也提供针对List里面嵌套对象的校验方式,...

    一 简介

    • 后端开发接口,经常会需要校验接口,可以使用spring提供的validation框架,使用注解进行校验,很方便。
    • 如果提供的注解满足不了业务需求,可以自己定义,也很方便;
    • 本文也提供针对List里面嵌套对象的校验方式,有些文章说解决不了,还是姿势不对。

     

    二 常用

    2.1 maven依赖

     TODO

    2.2 注解说明

    注解作用
    @Null限制只能为null
    @NotNull限制必须不为null
    @NotEmpty只作用于字符串类型,字符串不为空,并且长度不为0
    @NotBlank只作用于字符串类型,字符串不为空,并且trim()后不为空串
    @AssertFalse限制必须为false
    @AssertTrue限制必须为true
    @DecimalMax(value)限制必须为一个不大于指定值的数字
    @DecimalMin(value)限制必须为一个不小于指定值的数字
    @Digits(integer,fraction)限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction
    @Future限制必须是一个将来的日期
    @Past验证注解的元素值(日期类型)比当前时间早
    @Max(value)限制必须为一个不大于指定值的数字
    @Min(value)限制必须为一个不小于指定值的数字
    @Pattern(value)限制必须符合指定的正则表达式
    @Size(max,min)限制字符长度必须在min到max之间
    @Email验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式
      
      

     

    2.3 用法说明

    2.3.1 在接口字段参数上直接使用

    • 在方法字段参数上直接用对应的注解即可
    • 
      @RestController
      @RequestMapping("/tracemain")
      public class TraceMainController {
      
          @PostMapping("/save")
          public ResponseEntity<TraceMainSaveOutVo> saveTraceMain(
              @NotNull(message = "type字段不能为空") Integer type) {
              // xxxx
              return ResponseEntity.success(traceMainSaveOutVo);
          }
      
      }
      
      

       

     

    2.3.2 在对象上使用

    • Controller方法中对象前面添加 @Validated 注解,VO对象里面字段上面添加对应注解即可 
    • 如下代码
    
    @RestController
    @RequestMapping("/tracemain")
    public class TraceMainController {
    
        @PostMapping("/save")
        public ResponseEntity<TraceMainSaveOutVo> saveTraceMain(@RequestBody @Validated TraceMainSaveInVo traceMainSaveInVo) {
            // xxxx
            return ResponseEntity.success(traceMainSaveOutVo);
        }
    
    }
    
    
    
    @JsonInclude(JsonInclude.Include.NON_NULL)
    public class TraceMainSaveInVo extends SysModel {
    
        private static final long serialVersionUID = -1826896482893289340L;
    
        @ApiModelProperty("物流唯一单号")
        @NotBlank(message = "物流唯一单号不能为空")
        private String logisticsUniqueNo;
    
        @ApiModelProperty("HQ跟踪单号,格式为:HQ1+19位数字")
        @Pattern(regexp = "^HQ1+\\d{19}", message = "HQ跟踪单号格式有误,格式为:HQ1+19位数字")
        @Size(min = 22, max = 22, message = "HQ跟踪单号格式有误, 格式为:HQ1+19位数字")
        private String hqTraceNo;
    
    
        @ApiModelProperty("预报来源: 1-WMS,2-LMS, 3-1track")
        @NotNull(message = "预报来源不能为空")
        @Min(value = 1, message = "预报来源的值只能为1,2,3")
        @Max(value = 3, message = "预报来源的值只能为1,2,3")
        private Integer predictSource;
    
    }
    

     

    2.3.3 在嵌套对象使用

    • Controller类上添加 @Validated 注解,方法参数上添加 @Valid 注解,VO对象里面字段上面添加对应注解即可 ;
    • 代码如下:
    
    @RestController
    @RequestMapping("/xxx")
    @Validated
    public class PredictController {
    
        @PostMapping("/save")
        public ResponseEntity<List<PredictOutVo>> saveOrUpdatePredict(@RequestBody @Valid List<PredictInVo> predictInVoList) {
            // xxx
            return ResponseEntity.success(predictOutVoList);
        }
    
    }
    
    public class PredictInVo extends SysModel {
    
        @NotBlank(message = "OMS订单编号oms_order_no不能为空")
        private String omsOrderNo;
    
    }
    

     

    三  自定义

    3.0 场景

    • 目前我需要校验一个Integer类型的参数,是不连续的,如: 只能是1,2,3,6。目前提供的都解决不了,则需要自定义。

    3.1 自定义注解

    
    import javax.validation.Constraint;
    import javax.validation.Payload;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target({ElementType.METHOD, ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = IntegerEnumValidator.class)
    public @interface IntegerEnum {
    
        String message();
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    
        int[] intValues();
    
    
    }

     

    3.2 自定义校验器

    • value就是实际参数传递过来的值, intValues数组是我们定义的合法的参数范围数组;
    • 关于怎么找到的变量获取方法,不断debug即可知道如何获取;
    
    
    import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorContextImpl;
    
    import javax.validation.ConstraintValidator;
    import javax.validation.ConstraintValidatorContext;
    import javax.validation.metadata.ConstraintDescriptor;
    import java.util.Map;
    
    public class IntegerEnumValidator implements ConstraintValidator<IntegerEnum, Object> {
    
        private final String intVaules = "intValues";
    
        @Override
        public boolean isValid(Object value, ConstraintValidatorContext context) {
            if(null == value) {
                return true;
            }
            ConstraintDescriptor<?> constraintDescriptor = ((ConstraintValidatorContextImpl) context).getConstraintDescriptor();
            Map<String, Object> attributes = constraintDescriptor.getAttributes();
            int[] intValues = (int[]) attributes.get(intVaules);
            boolean hasFlag = false;
            for (int intValue : intValues) {
                if (intValue == ((int) value)) {
                    hasFlag = true;
                    break;
                }
            }
            return hasFlag;
        }
    }
    

    3.3 全局异常处理(可忽略)

    • 有些时候需要全局异常处理,显示异常信息,可以根据自己内部定义的异常处理进行获取。本文是使用的springboot,就直接写在了启动类中,如下:
    
    
    @SpringBootApplication
    public class TssAcquireApplication  {
    
    
        /**
         * spring validation校验异常处理
         */
        @ControllerAdvice
        class TssApiExceptionHandler {
            @ExceptionHandler(ConstraintViolationException.class)
            public void replayException(ConstraintViolationException replayException) {
                Set<ConstraintViolation<?>> constraintViolations = replayException.getConstraintViolations();
                if (!CollectionUtils.isEmpty(constraintViolations)) {
                    StringBuilder errorInfo = new StringBuilder();
                    constraintViolations.forEach(eachConstraintViolation -> {
                        errorInfo.append(eachConstraintViolation.getMessageTemplate()).append(";");
                    });
                    ResponseEntity<Object> responseEntity = ResponseEntity.fail(errorInfo.toString());
                    HttpOutJson.out(responseEntity, HttpStatus.BAD_REQUEST.value());
                }
            }
        }
    
    
    }
    

    3.4 使用

    • 和2里面的使用方式一样,只不过字段上注解使用我们自定义的注解即可;
        @Valid
        @NotNull(message = "货运类型不能为空")
        @IntegerEnum(intValues = {0, 1, 2, 3, 4, 5, 6, 7, 9}, message = "货运类型transport_type只能是0,1,2,3,4,5,6,7,9之一")
        private Integer transportType;

     

    展开全文
  • 使用spring validation完成数据后端校验

    万次阅读 多人点赞 2017-08-16 15:52:47
    而万能的spring为了给开发者提供便捷,对hibernate validation进行了二次封装,显示校验validated bean时,你可以使用spring validation或者hibernate validation,而spring validation另一个特性,便是其在...
  • 参考 ...import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.R
  • 主要介绍了详解使用spring validation完成数据后端校验,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • Spring Validation 参数校验 学习笔记

    千次阅读 2020-08-09 15:45:13
    Spring Boot:Spring Validation数据校验 springboot全局异常捕获统一处理及整合Validation Spring Boot 全局异常处理 与 Hibernate Validator校验框架整合 Hibernate Validator JSR 303 的参考实现 ...
  • 1、什么是Bean Validation JSR-303(JSR是Java Specification Requests的缩写,意思是Java 规范提案) 是JAVA EE 6 中的一项子规范,叫做Bean Validation,Hibernate Validator 是 Bean Validation 的参考实现。...
  • spring validation校验 List<String>

    千次阅读 2020-12-23 15:35:32
    方式一:spring maven 依赖: <dependency> <groupId>javax.el</groupId> <artifactId>javax.el-api</artifactId> <version>3.0.0</version> </dependency>...
  • Spring Validation的3种执行校验方式

    万次阅读 2019-04-21 11:53:43
    Validation的注解就不介绍了,大家可以去看源码包或者网上找相关资料。 这里直接说执行校验的3种方式。本文使用的参数注解情况以及请求的参数值如下 /** * 用户类 * @author z_hh * @time 2019年4月21日 ...
  • 而万能的spring为了给开发者提供便捷,对hibernate validation进行了二次封装,显示校验validated bean时,你可以使用spring validation或者hibernate validation,而spring validation另一个特性,便是其在...
  • Spring Validation实现原理分析

    千次阅读 2018-07-08 08:22:01
    即需要分析提交数据字段定义信息后才能明确对应的具体字段类型,进而做数据类型转换和字段有效性校验,然后做业务处理后提交数据库,自己开发一套校验逻辑的话周期太长,因此分析了Spring Validation的实现原理,...
  • 基于spring validation多层对象校验

    千次阅读 2018-09-18 14:36:09
    1、第一层对象定义 ...import javax.validation.Valid; /** * 请求参数 * @Title: ReqIn.java * @Package com.spring.pro.model * @Description: * @author ybwei * @date 2018年9...
  • 今天小编就为大家分享一篇关于Spring自带的校验框架Validation的使用实例,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
  • 1. 概述本文介绍在Spring Boot中实现对请求的数据进行校验。数据校验常用到概念: JSR303/JSR-349 1. 演示spring boot validation + 异常捕获机制实现数据自动校验功能 2. 自定义校验注解,并演示这个用法
  • 在介绍Spring Validation验证框架之前,先看一下我们常用的校验注解都有哪些 限制 说明 @Null 限制只能为null @NotNull 限制必须不为null @AssertFalse 限制必须为false @AssertTrue 限制必须为true ...
  • 而万能的spring为了给开发者提供便捷,对hibernate validation进行了二次封装,显示校验validated bean时,你可以使用spring validation或者hibernate validation,而spring validation另一个特性,便是其在...
  • Spring Validation及消息国际化.md

    千次阅读 热门讨论 2018-09-19 09:51:23
    JSR-303为这类校验提供了一个规范,并在JDK1.6起即提供了Validation包,其中包含了关键的ValidatorFactory及Validator、常用校验注解等内容。Hibernate又在其基础上提供了一个实现。注意这个实现与Hibernate数据库...
  • 引入依赖 我们使用maven构建springboot应用来进行demo演示。 <dependencies> <dependency> <groupId>org.springframework.boot</groupId>...spring-boot-starter-...
  • 快速失败(Fail Fast) Spring Validation

    千次阅读 2020-08-13 14:37:18
    @Bean ... ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class) .configure() // 快速失败模式 .failFast(true) .buildValidatorFactory(); return valida.
  • 【SpringBoot】Spring Validation 分组校验

    千次阅读 2019-07-26 22:39:40
    Spring自己有提供对Validation的支持,具体使用方法参见: https://blog.csdn.net/mzh_cn/article/details/80637015 这篇文章主要用于记录Spring Validation的分组校验。 在实际开发中,难免遇到对一个实体类的...
  • https://blog.csdn.net/qq_31417669/article/details/85046415
  • 作者:伍陆七https://juejin.cn/post/6856541106626363399之前也写过一篇关于Spring Validation使用的文章,不过自我感觉还是浮于表面,...
  • 一:pom依赖 <dependency>...javax.validation</groupId> <artifactId>validation-api</artifactId> <version>1.1.0.Final</version> </dependency&g...
  • spring validation校验对象多个字段返回的消息内容顺序随机问题
  • spring validation 校验(校验顺序)

    万次阅读 2019-04-19 14:54:09
    但是validation在验证时当多个参数同时出现校验异常时返回的错误是随机的,这个时候我们想要实现顺序验证应该怎么做呢。下面我们就一起探讨一下校验的顺序: validation在设计时已经考虑到了这种问题,提出了...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 69,674
精华内容 27,869
关键字:

springvalidation

spring 订阅