Springbootg整合validation整合

坚持年年写博客,不能断了,所以粘贴平时写的一份笔记吧

一、简介

校验参数在以前基本都是使用大量的if/else,稍微方便一点的可以使用反射+自定义注解的形式,但是复用性不是很好,并且每个人对于的自定义注解有着自己的使用习惯,不过好在spring开发了validated框架用于注解校验,可以节省很多的校验ifelse代码。 

  @PostMapping("/save")
    public Object save(@RequestBody User user) {
        
        String mobile = userVO.getMobile();
 
        //手动逐个 参数校验~ 写法
        if (StringUtils.isBlank(mobile)) {
            return R.bulid(ResultEnum.PARAM_FAIL_CODE,"mobile:手机号码不能为空");
        } else if (!Pattern.matches("^[1][3,4,5,6,7,8,9][0-9]{9}$", mobile)) {
            return R.bulid(ResultEnum.PARAM_FAIL_CODE,"mobile:手机号码格式不对");
        }
 
        //抛出自定义异常等~写法
        if (StringUtils.isBlank(userVO.getUsername())) {
            throw new ParamException(ResultEnum.PARAM_FAIL_CODE, "用户名不能为空");
        }
 
        // 比如写一个map返回
        if (StringUtils.isBlank(userVO.getSex())) {
            Map<String, Object> result = new HashMap<>(5);
            result.put("code", Constant.PARAM_FAIL_CODE);
            result.put("msg", "性别不能为空");
            return result;
        }
        //.........各种写法 ...
        userService.save(user);
        return R.success();
    }

二、Spring Validation

spring Validation 是一种参数检验工具,集成在spring-context包中, 常用于spring mvcController的参数处理,主要针对整个实体类的多个可选域进行判定,对于不合格的数据信息springMVC会把它保存在错误对象中,这些错误信息我们也可以通过SpringMVC提供的标签或者前端的脚本等在前端页面上进行展示。

1、实现方式

实现方式和使用方式:一般使用较多的是两个注解:@Validated@Valid

  • @Valid和@Validated 两种注释都会导致应用标准Bean验证。

  • 如果验证不通过会抛出BindException异常,并变成400(BAD_REQUEST)响应;

  • 或者可以通过Errors或BindingResult参数在控制器内本地处理验证错误。

  • 如果参数前有@RequestBody注解,验证错误会抛出MethodArgumentNotValidException异常。

2、Java Bean Validation

JSR是Java Specification Requests的缩写,意思是Java 规范提案。关于数据校验这块,最新的是JSR380,也就是我们常说的Bean Validation 2.0。

Bean Validation 2.0 是JSR第380号标准。该标准连接如下:The Java Community Process(SM) Program - Community Update - View Community Update Information for JSR# 380

Bean Validation的主页:Jakarta Bean Validation - Home

Bean Validation的参考实现:https://github.com/hibernate/hibernate-validator

Bean Validation是一个通过配置注解来验证参数的框架,它包含两部分Bean Validation API(规范)和Hibernate Validator(实现)。

Bean Validation是Java定义的一套基于注解/xml的数据校验规范,目前已经从JSR 303的1.0版本升级到JSR 349的1.1版本,再到JSR 380的2.0版本(2.0完成于2017.08),已经经历了三个版本

3、@Valid@Validated区别

可以理解成@Validated是@Valid升级版

区别@Valid@Validated
提供者JSR-303规范Spring(validator-api-2.0)
是否支持分组不支持支持
标注位置METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USETYPE, METHOD, PARAMETER
嵌套校验支持不支持

Spring最终是调用Hibernate Validator执行校验,Spring Validation只是做了一层封装。

通过源码分析:

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Valid {
}
​
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Validated {
    Class<?>[] value() default {};
}

@Valid:没有分组的功能。

@Valid:可以用在方法、构造函数、方法参数和成员属性(字段)上

@Validated:提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制

@Validated:可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上

关于分组功能,此处我们不涉及,有兴趣的同学可以自己研究一下

3、常用注解

validator-api-2.0的约束注解有22个,具体我们看下面表格

  • 空与非空检查

注解支持Java类型说明
@NullObject为null
@NotNullObject不为null
@NotBlankCharSequence不为null,且必须有一个非空格字符
@NotEmptyCharSequence、Collection、Map、Array不为null,且不为空(length/size>0)

@NotNull:适用于基本数据类型(Integer,Long,Double等等),当 @NotNull 注解被使用在 String 类型的数据上,则表示该数据不能为 Null(但是可以为 Empty,即双引号空) @NotBlank:适用于 String 类型的数据上,加了@NotBlank 注解的参数不能为 Null 且 trim() 之后 size > 0 @NotEmpty:适用于 String、Collection集合、Map、数组等等,加了@NotEmpty 注解的参数不能为 Null 或者 长度为 0

  • Boolean值检查

注解支持Java类型说明备注
@AssertTrueboolean、Boolean为true为null有效
@AssertFalseboolean、Boolean为false为null有效
  • 日期检查

注解说明备注
@Future验证日期为当前时间之后为null有效
@FutureOrPresent验证日期为当前时间或之后为null有效
@Past验证日期为当前时间之前为null有效
@PastOrPresent验证日期为当前时间或之前为null有效
  • 数值检查

注解说明备注
@Max小于或等于为null有效
@Min大于或等于为null有效
@DecimalMax小于或等于为null有效
@DecimalMin大于或等于为null有效
@Negative负数为null有效,0无效
@NegativeOrZero负数或零为null有效
@Positive正数为null有效,0无效
@PositiveOrZero正数或零为null有效
@Digits(integer = 3, fraction = 2)整数位数和小数位数上限为null有效
  • 其他

注解说明备注
@Pattern匹配指定的正则表达式为null有效
@Email邮箱地址为null有效,默认正则 '.*'
@Size大小范围(length/size>0)为null有效
  • hibernate-validator扩展约束(部分)

注解支持Java类型说明
@LengthString字符串长度范围
@Range数值类型和String指定范围
@URLURL地址验证

综合在一起,对于不同的api版本可能出现部分注解过时等情况,注意!

meta-datacommentversion
@Null对象,为空Bean Validation 1.0
@NotNull对象,不为空Bean Validation 1.0
@AssertTrue布尔,为TrueBean Validation 1.0
@AssertFalse布尔,为FalseBean Validation 1.0
@Min(value)数字,最小为valueBean Validation 1.0
@Max(value)数字,最大为valueBean Validation 1.0
@DecimalMin(value)数字,最小为valueBean Validation 1.0
@DecimalMax(value)数字,最大为valueBean Validation 1.0
@Size(max, min)min<=value<=maxBean Validation 1.0
@Digits (integer, fraction)数字,某个范围内Bean Validation 1.0
@Past日期,过去的日期Bean Validation 1.0
@Future日期,将来的日期Bean Validation 1.0
@Pattern(value)字符串,正则校验Bean Validation 1.0
@Email字符串,邮箱类型Bean Validation 2.0
@NotEmpty集合,不为空Bean Validation 2.0
@NotBlank字符串,不为空字符串Bean Validation 2.0
@Positive数字,正数Bean Validation 2.0
@PositiveOrZero数字,正数或0Bean Validation 2.0
@Negative数字,负数Bean Validation 2.0
@NegativeOrZero数字,负数或0Bean Validation 2.0
@PastOrPresent(时间)过去或者现在Bean Validation 2.0
@FutureOrPresent(时间)将来或者现在Bean Validation 2.0

三、通过BindingResult处理错误信息

使用Validator,利用BindingResult获取Errors信息

1、pom引入依赖

只需要spring-boot-starter-validation和web即可

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

2、修改实体类User

package com.woniuxy.ssm.entity;
​
import java.io.Serializable;
import java.util.Date;
​
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.Range;
import org.springframework.format.annotation.DateTimeFormat;
​
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
​
/**
 * 
 * @TableName user
 */
@Data
public class User implements Serializable {
    /**
     * 
     */
    private Integer id;
​
    /**
     * 用户名
     */
    @NotBlank(message = "用户名不能为空")
    @Length(message = "用户名长度不能超过{max}个字符",max = 10)
    private String userName;
​
    /**
     * 电话
     */
    @Pattern(regexp = "^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\\d{8}$",message = "手机号码有误!")
    private String tel;
    /**
     * 密码
     */
    @NotBlank(message = "密码不能为空")
    @Size(message = "密码不小于{min}位", min= 8)
    private String password;
    /**
     * 年龄
     */
    @NotNull(message = "请输入年龄")
    @Range(message = "年龄范围为 {min} 到 {max} 之间", min = 1, max = 100)
    private Integer age;
​
    /**
     * 
     */
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @JsonFormat(pattern = "yyyy-MM-dd" ,timezone = "GMT+8")
    private Date createDate;
​
    /**
     * 
     */
    private String headImg;
​
    /**
     * 
     */
    private Integer deptId;
​
    private static final long serialVersionUID = 1L;
}

3、修改UserController中的add方法

得到所有错误中的第一个返回给用户显示,第一个并不一定是固定的,如果密码长度和年龄同时不满足,多次查询返回的结果并不是固定的,且不会超过这两个错误信息

/**
     * @Description TODO
     * @Valid 表示对这个对象校验
     * @param user
     * @param bindingResult  获取的是校验的结果,这个对象有许多方法获取校验信息,可以自定义返回信息
     * @Return com.woniuxy.ssm.util.Result
     * @Author  fengSir
     * @Date    Create by 2022-06-12 14:58
     */
    @PostMapping("addDo")
    public Result add(@Valid @RequestBody User user, BindingResult bindingResult){
        if(bindingResult.hasErrors()){
            return Result.error(bindingResult.getFieldError().getDefaultMessage());
        }
        int result = userService.insertSelective(user);
        return Result.ok();
    }

我们也可以@Valid换成@Validated,两者效果相同

4、通过测试工具类测试

三、使用全局异常处理错误信息

虽然使用bindingResult可以解决校验问题,但代码侵入性太强。此时我们可以考虑采用全局异常来解决

1、改造UserController中的add方法

   @PostMapping("addDo")
    public Result add(@Validated @RequestBody User user){
        int result = userService.insertSelective(user);
        return Result.ok();
    }

2、通过测试工具测试

由于参数里有@RequestBody注解,验证错误会抛出MethodArgumentNotValidException异常

3、自定义全局异常

package com.woniuxy.ssm.exception;
​
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
​
/**
 * @author :fengSir
 * @date :Created By 2022-06-12 16:12
 * @description :TODO
 */
@RestControllerAdvice
public class ValidExceptionHandler {
​
    @ExceptionHandler(BindException.class)
    public Result validExceptionHandler(BindException exception) {
        return Result.error(exception.getBindingResult().getFieldError().getDefaultMessage());
    }
​
}

4、再次测试

得到和以前一样的测试结果

四、@Valid 和 @Validated 比较

对 @Valid 和 @Validated 两个注解进行总结下:

  1. @Valid 和 @Validated 两者都可以对数据进行校验,待校验字段上打的规则注解(@NotNull, @NotEmpty等)都可以对 @Valid 和 @Validated 生效;

  2. @Valid 进行校验的时候,需要用 BindingResult 来做一个校验结果接收。当校验不通过的时候,如果手动不 return ,则并不会阻止程序的执行;

  3. @Validated 进行校验的时候,也可以用 BindingResult 来做一个校验结果接收。当校验不通过的时候,程序会抛出400异常,阻止方法中的代码执行,这时需要再写一个全局校验异常捕获处理类,然后返回校验提示。

  4. 总体来说,@Validated 使用起来要比 @Valid 方便一些,它可以帮我们节省一定的代码,并且使得方法看上去更加的简洁。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值