Jakarta Bean Validation学习

阅读数:85 评论数:0

跳转到新版页面

分类

python/Java

正文

一、概述

在java校验中,为了简化开发,JavaEE 6中一项子规范JSR303,叫作Bean validator。

Jakarta是Java更名之后的名称,Jakarta Bean Validation也就是Java Bean Validation,是一套Java的规范。

1、@Validated

@Validated是Spring框架中的验证注解。

它可用在类型、方法、方法参数上,但不能用在成员属性上,不支持嵌套检测,支持分组功能。

2、@Valid

@Valid是java标准库中的验证注解。java的jsr303声明了@Valid接口,而hibernate-vlidator对其进行了实现。

可用在方法、方法参数上、成员属性上,支持嵌套检测,不支持分组功能。

二、Spring boot中使用Bean Validation

1、pom依赖

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>
<dependency>
    <groupId>jakarta.validation</groupId>
    <artifactId>jakarta.validation-api</artifactId>
    <version>2.0.1</version>
</dependency>
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.1.5.Final</version>
</dependency>

它们的关系是,Hibernate Validator是Bean Validation的实例,除了JSR规范中的,还加入了它自己的一些constraint实例,所以它依赖于validation-api,jakarta.validation由javax.validation改名而来。

其实我个人理解,可以不用依赖

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>

对于spring boot 应用,直接引用它提供的starter

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

2、常用注解

Constraint 说明 支持的数据类型
@AssertFalse 被注释的元素必须为 false Boolean
@AssertTrue 被注释的元素必须为 true Boolean
@DecimalMax 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 BigDecimal, BigInteger, CharSequence, byte, short, int, long
@DecimalMin 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 BigDecimal, BigInteger, CharSequence, byte, short, int, long
@Max 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 BigDecimal, BigInteger, byte, short, int, long
@Min 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 BigDecimal, BigInteger, byte, short, int, long
@Digits(integer=, fraction=) 检查注释的值是否为最多为整数位(integer)和小数位(fraction)的数字 BigDecimal, BigInteger, CharSequence, byte, short, int, long
@Email 被注释的元素必须是电子邮箱地址,可选参数 regexp和flag允许指定必须匹配的附加正则表达式(包括正则表达式标志)。 CharSequence
@Future 被注释的元素必须是一个将来的日期 Date,Calendar,Instant,LocalDate等
@FutureOrPresent 被注释的元素必须是一个将来的日期或现在的日期 Date,Calendar,Instant,LocalDate等
@Past 被注释的元素必须是一个过去的日期 Date,Calendar,Instant,LocalDate等
@PastOrPresent 被注释的元素必须是一个过去的日期或现在的日期 Date,Calendar,Instant,LocalDate等
@NotBlank 被注释的元素不为null,并且去除两边空白字符后长度大于0 CharSequence
@NotEmpty 被注释的元素不为null,并且集合不为空 CharSequence, Collection, Map, arrays
@NotNull 被注释的元素不为null Any type
@Null 被注释的元素为null Any type
@Pattern(regex=, flags=) 被注释的元素必须与正则表达式 regex 匹配 CharSequence
@Size(min=, max=) 被注释的元素大小必须介于最小和最大(闭区间)之间 CharSequence, Collection, Map,arrays

3、全局异常处理

若不做异常处理,@Validated注解的默认异常消息如下:

2020-09-05 21:48:38.106  WARN 9796 --- [nio-8080-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public java.lang.String com.example.validateddemo.controller.DemoController.validatedDemo1(com.example.validateddemo.entity.dto.UseDto): [Field error in object 'useDto' on field 'username': rejected value [null]; codes [NotBlank.useDto.username,NotBlank.username,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [useDto.username,username]; arguments []; default message [username]]; default message [用户名不能为空!]] ]

全局异常处理示例

package com.example.validateddemo.handler;
 
import com.example.validateddemo.base.Result;
import com.example.validateddemo.enums.ResultEnum;
import com.example.validateddemo.utils.ResultUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import java.util.List;
 
/**
 * @author He Changjie on 2020/9/5
 */
@Slf4j
@ControllerAdvice
public class ValidatedExceptionHandler {
 
    /**
     * 处理@Validated参数校验失败异常
     * @param exception 异常类
     * @return 响应
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result exceptionHandler(MethodArgumentNotValidException exception){
        BindingResult result = exception.getBindingResult();
        StringBuilder stringBuilder = new StringBuilder();
        if (result.hasErrors()) {
            List<ObjectError> errors = result.getAllErrors();
            if (errors != null) {
                errors.forEach(p -> {
                    FieldError fieldError = (FieldError) p;
                    log.warn("Bad Request Parameters: dto entity [{}],field [{}],message [{}]",fieldError.getObjectName(), fieldError.getField(), fieldError.getDefaultMessage());
                    stringBuilder.append(fieldError.getDefaultMessage());
                });
            }
        }
        return ResultUtil.validatedException(stringBuilder.toString());
    }
}

4、基础参数检验

(1)实体类

package com.example.validateddemo.entity.dto;
 
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
 
import javax.validation.constraints.*;
 
/**
 * 用户实体
 * 数据传输对象
 * @author He Changjie on 2020/9/5
 */
@Data
public class User1Dto {
    /**
     * 用户名
     */
    @NotBlank(message = "用户名不能为空!")
    private String username;
    /**
     * 性别
     */
    @NotBlank(message = "性别不能为空!")
    private String gender;
    /**
     * 年龄
     */
    @Min(value = 1, message = "年龄有误!")
    @Max(value = 120, message = "年龄有误!")
    private int age;
    /**
     * 地址
     */
    @NotBlank(message = "地址不能为空!")
    private String address;
    /**
     * 邮箱
     */
    @Email(message = "邮箱有误!")
    private String email;
    /**
     * 手机号码
     */
    @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 mobile;
}

(2)控制类

package com.example.validateddemo.controller;
 
import com.example.validateddemo.entity.dto.Use1Dto;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
/**
 * @author He Changjie on 2020/9/5
 */
@RestController
@RequestMapping("/api/v1")
public class Demo1Controller {
 
    @PostMapping("/insert")
    public String validatedDemo1(@Validated @RequestBody Use1Dto use1Dto){
        System.out.println(use1Dto);
        return "success";
    }
}

5、嵌套参数验证

(1)实体类

package com.example.validateddemo.entity.dto;
 
import lombok.Data;
 
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.List;
 
/**
 * 队伍实体
 * 数据传输对象
 * @author He Changjie on 2020/9/5
 */
@Data
public class Team1Dto {
    /**
     * 队伍名称
     */
    @NotBlank(message = "队伍名称不能为空!")
    private String name;
    /**
     * 队伍人员,结合@Valid做嵌套
     */
    @NotNull(message = "队伍人员不能为空!")
    @Valid
    private List<User1Dto> userList;
 
    /**
     * 队伍负责人
     */
    @NotNull(message = "队伍负责人不能为空!")
    @Valid
    private User1Dto user;
}

(2)控制类

package com.example.validateddemo.controller;
 
import com.example.validateddemo.base.Result;
import com.example.validateddemo.entity.dto.Team1Dto;
import com.example.validateddemo.entity.dto.Use1Dto;
import com.example.validateddemo.utils.ResultUtil;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
/**
 * @author He Changjie on 2020/9/5
 */
@RestController
@RequestMapping("/api/v1")
public class Demo1Controller {
 
    @PostMapping("/insert")
    public Result validatedDemo1(@Validated @RequestBody Use1Dto use1Dto){
        return ResultUtil.success(use1Dto);
    }
 
    @PostMapping("/insert2")
    public Result validatedDemo2(@Validated @RequestBody Team1Dto team1Dto){
        return ResultUtil.success(team1Dto);
    }
}

6、分组参数验证

将不同的校验规则分给不同的组,在使用时,指定不同的校验规则,可以用于更新和新增接口不同的校验规则

(1)分组接口

package com.example.validateddemo.interfaces;
 
/**
 * 校验分组1
 * @author He Changjie on 2020/9/5
 */
public interface Group1 {
}

package com.example.validateddemo.interfaces;
 
/**
 * 校验分组2
 * @author He Changjie on 2020/9/5
 */
public interface Group2 {
}

(2)实体类

package com.example.validateddemo.entity.dto;
 
import com.example.validateddemo.interfaces.Group1;
import com.example.validateddemo.interfaces.Group2;
import lombok.Data;
 
import javax.validation.constraints.*;
 
/**
 * @author He Changjie on 2020/9/5
 */
@Data
public class User2Dto {
    /**
     * 用户名
     */
    @NotBlank(message = "用户名不能为空!", groups = {Group1.class})
    private String username;
    /**
     * 性别
     */
    @NotBlank(message = "性别不能为空!")
    private String gender;
    /**
     * 年龄
     */
    @Min(value = 1, message = "年龄有误!", groups = {Group1.class})
    @Max(value = 120, message = "年龄有误!", groups = {Group2.class})
    private int age;
    /**
     * 地址
     */
    @NotBlank(message = "地址不能为空!")
    private String address;
    /**
     * 邮箱
     */
    @Email(message = "邮箱有误!", groups = {Group2.class})
    private String email;
    /**
     * 手机号码
     */
    @Pattern(regexp = "^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\\d{8}$",message = "手机号码有误!", groups = {Group2.class})
    private String mobile;
}

(3)控制类

package com.example.validateddemo.controller;
 
import com.example.validateddemo.base.Result;
import com.example.validateddemo.entity.dto.Team1Dto;
import com.example.validateddemo.entity.dto.User1Dto;
import com.example.validateddemo.entity.dto.User2Dto;
import com.example.validateddemo.interfaces.Group1;
import com.example.validateddemo.interfaces.Group2;
import com.example.validateddemo.utils.ResultUtil;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
/**
 * @author He Changjie on 2020/9/5
 */
@RestController
@RequestMapping("/api/v1")
public class Demo1Controller {
 
    @PostMapping("/insert")
    public Result validatedDemo1(@Validated @RequestBody User1Dto user1Dto){
        return ResultUtil.success(user1Dto);
    }
 
    @PostMapping("/insert2")
    public Result validatedDemo2(@Validated @RequestBody Team1Dto team1Dto){
        return ResultUtil.success(team1Dto);
    }
 
    @PostMapping("/insert3")
    public Result validatedDemo3(@Validated @RequestBody User2Dto user2Dto){
        return ResultUtil.success(user2Dto);
    }
 
    @PostMapping("/insert4")
    public Result validatedDemo4(@Validated(Group1.class) @RequestBody User2Dto user2Dto){
        return ResultUtil.success(user2Dto);
    }
 
    @PostMapping("/insert5")
    public Result validatedDemo5(@Validated(Group2.class) @RequestBody User2Dto user2Dto){
        return ResultUtil.success(user2Dto);
    }
}



相关推荐

一、概述 springboot中有两种方式使用kafka时,直接使用kafka-client连接kafka服务;另一种是使用spring-kafka框架来连接kafka。 1、版本兼容 使用时要注意版

当然可以自己写redis的工具类,或者使用第三方开源jar包或代码,这里使用spring boot的集成类。 一、pom依赖 <dependency> <gro

websocket协议基于tcp的网络协议,它实现浏览器与器全双工通信。 spring boot2 +websocket 1、添加依赖 <pre clas

背景: 之前用spring boot+mybatis+oracle,现在要改成spring boot_mybatis+postgresql。 为了想让一套代码即可

共同点 都是用来表示Spring某个类是否可以接收HTTP请求。 不同点 @Controller标识一个spring类是Spring MVC c

系统异常捕获 参见:spring boot 2 全局异常处理 @ControllerAdvice(annotations = {RestController.class}) public class

从SSH(Structs/Spring/Hibernate)到SSM(SpringMVC/Spring/MyBatis),到现在一个S(Spring)就够的年代,可见Spring越来越重要了。<

@ConditionalOnMissingBean只能在@Bean注解的方法上使用。 可以给该注解传入参数例如@ConditionOnMissingBean(name = "exa

2.4版本之前 在之前,我们在yaml配置文件中,使用spring.profiles来定义不同环境的标识,比如下面这样: spring: profiles: "dev" name: dev.di

在SpringBoot框架中,我们使用最多的是Tomcat,这是SpringBoot默认的容器技术,而且是内嵌式的Tomcat。   同时,SpringBoot也支持Undertow容器,Undert