springboot valid 实现数据的校验,同一对象下相同日期属性之间的比较校验

目录

使用初衷

实现流程

常用的校验注解

代码实现:

1.controller 层对象加上@valid 即可

2.在你需要校验的对象属性上增加校验注解

3.全局异常处理

常用的属性校验数据方式(不能使用不同校验注解来实现)

第一种:借助@Pattern

第二种:借助自定义注解方式

第一步:定义一个注解 CompareDateValid

第二步:实现 ConstraintValidator 接口,并不是刚才定义的接口

第三步:在对象上使用自定义注解


使用初衷

如果多个表单提交,不管前端是否进行数据校验,后台接口中一定要对重要数据进行校验以及数据库字段设置,因此,如果对每个表单进行数据校验,那代码写的比较繁琐,而且controller层一长串都是数据校验,如果一个表单对象有三十多个属性,自己也觉得写的想吐,所以,我们使用spring自带的数据校验框架vaildation。

实现流程

我们选择一种常用的校验方式,大致流程也比较简单和实用,请求接口-->controller层-->接收对象-->对象通过注解方式进行基本的数据校验-->如果校验不通过-->通过全局异常处理进行统一返回异常

常用的校验注解

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

代码实现:

注:RestResponse是我统一返回的对象封装

1.controller 层对象加上@valid 即可

@PostMapping("insert")
    public RestResponse<?> insert(@Valid UserInfo user){
        int insert = userService.insert(user);
        return RestResponse.success(insert);
    }

2.在你需要校验的对象属性上增加校验注解


    @NotNull(message = "请填写婚姻信息")
    private String marriage;

    @NotNull(message = "请填写民族")
    private String nation;

    @NotNull(message = "请填写籍贯")
    private String nativePlace;

    @NotNull(message = "请填写政治面貌")
    private String politic ;

    @Email(message = "邮箱地址错误")
    private String email ;

3.全局异常处理

/**
 * 全局异常处理中心
 * @author fangh
 */
@ControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);


    }

    /**
     * 校验错误拦截处理
     * @param bindException
     * @return {@link RestResponse}
     */
    @ExceptionHandler(value = BindException.class)
    @ResponseBody
    public RestResponse<?> schedulerException(BindException bindException){
        BindingResult bindingResult = bindException.getBindingResult();
        if(bindingResult.hasErrors()){
            List<ObjectError> errors = bindingResult.getAllErrors();
            for (ObjectError objectError:errors) {
                FieldError fieldError = (FieldError) objectError;
                logger.error("数据校验失败 : 对象【"+fieldError.getObjectName()
                        +"】中的【"+fieldError.getField()
                        + "】的属性校验错误,信息为:["+fieldError.getDefaultMessage()+"]");
                return RestResponse.fail(Commons.SysCode.vaild.getCode(),fieldError.getDefaultMessage());
            }
        }
        return  RestResponse.fail(Commons.SysCode.vaild.getCode(),bindException.getMessage());
    }

常用的属性校验数据方式(不能使用不同校验注解来实现)

第一种:借助@Pattern

直接在属性名上加入此注解,一般会和@NotNull 来配合使用,不然如果数据为空,直接跳过,当然你也可以在校验规则中加入不为空。

常用的校验规则:

校验手机号码:

@Pattern(regexp = "(^1(3|4|5|7|8)\\d{9}$)", message = "手机号码格式错误")

校验身份证号码:

@Pattern(regexp = "^(\\d{18,18}|\\d{15,15}|(\\d{17,17}[x|X]))$", message = "身份证格式错误")

第二种:借助自定义注解方式

这里我就不写简单的注解校验,写一种比较复杂的数据校验,当然这是我自己想出来的,搜了各种资料,发现没有相关的博客,有可能是我没找到,最后决定自己去实现,具体场景就是,比如一个职工,合同到期时间和系统登录时间肯定在入职时间前面,如果目前我找的资料暂时没有这种注解支持,如果有,请留言告知,谢谢!

第一步:定义一个注解 CompareDateValid

注:1.因为水准不行,但为了显示代码格调上一个层次,也就用自己英语一级水准写了注释,勿喷

        2.上面相关的注解都有中文说明,不需要你们查询各种资料

package com.along.interfaces;


import com.along.interfaces.impl.CompareDateImpl;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

/**
 * 日期校验
 * @Documented 注解表明这个注解应该被 javadoc工具记录. 默认情况下,javadoc是不包括注解的. 但如果声明注解时指定了
 * @Retention 1.RetentionPolicy.SOURCE —— 这种类型的Annotations只在源代码级别保留,编译时就会被忽略
 *            2.RetentionPolicy.CLASS —— 这种类型的Annotations编译时被保留,在class文件中存在,但JVM将会忽略
 *            3.RetentionPolicy.RUNTIME —— 这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的
 *              代码所读取和使用
 *@Target 作用范围 1.METHOD  可用于描述方法上
 *                2.TYPE 用于描述类、接口(包括注解类型)或enum声明上
 *                3.ANNOTATION——TYPE 用于注解类型上(被@interface修饰的类型)
 *                4.CONSTRUCTOR 用于描述构造器上
 *                5.FIELD  用于描述域
 *                6.LOCAL_VARIABLE  用于描述局部变量
 *                7.Package 用于记录java文件的package信息
 *                8.PARAMETER  	可用于参数上
 * @Constraint 指定了需要进行校验的策略类集合(即接口实现对应的类,实现的类必须要实现 ConstraintValidator 这个接口,并实现里面的
 *              initialize和isValid方法),具体实现请看 {@link com.along.interfaces.impl.CompareDateImpl}
 * @author fangh
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.TYPE})
@Constraint(validatedBy = {CompareDateImpl.class})
public @interface CompareDateValid {
    /**
     * It is must be to required
     * default false ,you can change data
     * allowed value : true , false
     */
    boolean required() default false;

    /**
     * It was error to show message when valited
     * show message to request
     * allowed value : anything
     */
    String message() default "日期应该在之后或者之前";

    /**
     *It is compare filed,you must to be write
     * allowed value : the field of the same object
     */
    String field() default "";

    /**
     * If you want to compare with filed of the same object,you can to write
     * you must be write the filed of the same object,or must be cause the error
     * allowed value : the filed of the same object except self
     */
    String[] compareField() default {};

    /**
     * If comapre time of after
     * defalut after of time for filed
     *
     */
    boolean compareAfter() default true;

    Class<?>[] groups() default { };

    Class<? extends Payload>[] payload() default { };

}

第二步:实现 ConstraintValidator 接口,并不是刚才定义的接口

重点:1.通过反射的方式获取相对应值对象属性

       2.重新定义新的信息模板,返回更改后的message值

package com.along.interfaces.impl;

import com.along.interfaces.CompareDateValid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * 日期校验
 * @author fangh
 */
public class CompareDateImpl implements ConstraintValidator<CompareDateValid,Object> {

    private static final Logger logger = LoggerFactory.getLogger(CompareDateImpl.class);

    private boolean required ;

    private String message;

    private String[] compareField;

    private String field;

    private boolean compareAfter;

    @Override
    public void initialize(CompareDateValid constraintAnnotation) {
        this.required = constraintAnnotation.required();
        this.message = constraintAnnotation.message();
        this.compareField = constraintAnnotation.compareField();
        this.field = constraintAnnotation.field();
        this.compareAfter = constraintAnnotation.compareAfter();
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        if(!required){
            if(value == null){
                return  true;
            }
        }

        try {
            //属性获取get方法属性,并获取对应的值
            Object invokeValue = getValueByGetMethod(value, field);
            if(invokeValue == null){
                return false;
            }
            Date fieldDate = (Date) invokeValue;

            //比较属性集合中的get方法,并获取相对应的值,且进行常规比较
            if(compareField  != null){
                for (String compareFields:compareField) {
                    Object compareInvokeValue = getValueByGetMethod(value, compareFields);
                    if(compareInvokeValue == null){
                        return false;
                    }
                    Date compareDate = (Date) compareInvokeValue;
                    this.message =field+"应该在"+compareFields+(compareAfter?"之后":"之前");
                    if(fieldDate.after(compareDate) != compareAfter){
                        context.disableDefaultConstraintViolation();
                        context.buildConstraintViolationWithTemplate(message).addPropertyNode(field).addConstraintViolation();
                        return false ;
                    }
                }
            }
            return true ;
        } catch (NoSuchMethodException e) {
            logger.error(e.getMessage());
            this.message = "请填写正确属性名称" ;
        } catch (IllegalAccessException e) {
            logger.error(e.getMessage());
            this.message = "请在拥有访问权限的类上加入CompareDate注解" ;
        } catch (InvocationTargetException e) {
            logger.error(e.getMessage());
            this.message = "注解所在的类get方法执行异常";
        }catch (ClassCastException e){
            logger.error(e.getMessage());
            this.message = "请填写正确的日期格式";
        }
        return false;
    }

package com.along.interfaces.impl;

import com.along.interfaces.CompareDateValid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * 日期校验
 * @author fangh
 */
public class CompareDateImpl implements ConstraintValidator<CompareDateValid,Object> {

    private static final Logger logger = LoggerFactory.getLogger(CompareDateImpl.class);

    private boolean required ;

    private String message;

    private String[] compareField;

    private String field;

    private boolean compareAfter;

    @Override
    public void initialize(CompareDateValid constraintAnnotation) {
        this.required = constraintAnnotation.required();
        this.message = constraintAnnotation.message();
        this.compareField = constraintAnnotation.compareField();
        this.field = constraintAnnotation.field();
        this.compareAfter = constraintAnnotation.compareAfter();
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        if(!required){
            if(value == null){
                return  true;
            }
        }

        try {
            //属性获取get方法属性,并获取对应的值
            Object invokeValue = getValueByGetMethod(value, field);
            if(invokeValue == null){
                return false;
            }
            Date fieldDate = (Date) invokeValue;

            //比较属性集合中的get方法,并获取相对应的值,且进行常规比较
            if(compareField  != null){
                for (String compareFields:compareField) {
                    Object compareInvokeValue = getValueByGetMethod(value, compareFields);
                    if(compareInvokeValue == null){
                        return false;
                    }
                    Date compareDate = (Date) compareInvokeValue;
                    this.message =field+"应该在"+compareFields+(compareAfter?"之后":"之前");
                    if(fieldDate.after(compareDate) != compareAfter){
                        context.disableDefaultConstraintViolation();
                        context.buildConstraintViolationWithTemplate(message).addPropertyNode(field).addConstraintViolation();
                        return false ;
                    }
                }
            }
            return true ;
        } catch (NoSuchMethodException e) {
            logger.error(e.getMessage());
            this.message = "请填写正确属性名称" ;
        } catch (IllegalAccessException e) {
            logger.error(e.getMessage());
            this.message = "请在拥有访问权限的类上加入CompareDate注解" ;
        } catch (InvocationTargetException e) {
            logger.error(e.getMessage());
            this.message = "注解所在的类get方法执行异常";
        }catch (ClassCastException e){
            logger.error(e.getMessage());
            this.message = "请填写正确的日期格式";
        }
        return false;
    }

    /**
     * 通过反射中get方法获取对象中的属性值
     * @param obj 对象
     * @param field 对象中的属性名称
     * @return 对象属性中的值
     * @throws InvocationTargetException 执行方法异常
     * @throws IllegalAccessException 访问权限异常
     * @throws NoSuchMethodException 没有对应的方法异常
     */
    public static Object getValueByGetMethod(Object obj,String field) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {

        String getMethod = "get"+field.substring(0, 1).toUpperCase()+field.substring(1, field.length());
        Method method = obj.getClass().getMethod(getMethod, new Class[]{});
        return method.invoke(obj, new Object[]{});
    }

}

}

第三步:在对象上使用自定义注解

@CompareDateValid(field = "contractExpireTime",compareField = {"hireTime"},required = false,compareAfter = true)

完成,请求接口你就可以开始测试了。

喜欢我的朋友,请点个赞,我会带来更多的容易理解的博客给各位!

SpringBoot2.x集成WebSocket,实现后台向前端(h5 页面,java客户端)推送信息

Mysql 替换某个字符串指定位置字符串
 
 

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot提供了很方便的数据校验功能,可以通过注解方式实现。要使用数据校验功能,首先需要在项目的pom.xml文件中添加相关的依赖。其中,我们可以使用spring-boot-starter-validation依赖来引入数据校验功能。 在代码中,我们可以使用javax.validation.constraints包中的注解来对数据进行校验。常用的注解包括@NotNull、@NotEmpty、@Size、@Min、@Max等。例如,使用@NotNull注解来标记一个字段不能为空。 首先,在需要校验的类中,添加注解@Validated,这样Spring Boot会自动校验被注解标记的字段。然后,在需要校验的字段上添加相关的校验注解,如@NotNull。最后,在Controller的方法参数上添加@Valid注解,来触发数据校验。 下面是一个示例代码,展示了如何在Spring Boot中进行数据校验。 ```java import org.springframework.validation.annotation.Validated; import javax.validation.constraints.NotNull; @RestController @Validated public class UserController { @PostMapping("/user") public String addUser(@Valid @RequestBody User user) { // 业务逻辑处理 // ... return "User added successfully"; } public class User { @NotNull private String username; // getters and setters } } ``` 在上述示例中,我们定义了一个UserController类,其中的addUser方法用于添加用户。在User类中,我们使用了@NotNull注解来标记username字段不能为空。在addUser方法的参数中,我们使用了@Valid注解来触发数据校验。 通过以上配置,当我们发送一个POST请求到"/user"的时候,Spring Boot会自动校验请求体中的数据。如果校验失败,会返回相应的错误信息。如果校验通过,会执行业务逻辑并返回成功信息。 总结一下,Spring Boot提供了简单易用的数据校验功能,可以通过注解方式实现。通过添加相应的依赖和配置,我们可以在项目中方便地使用数据校验功能,保证数据的有效性和完整性。 :https://www.jianshu.com/p/2dabf4f0b72e :https://www.baeldung.com/spring-boot-bean-validation

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值