Java参数校验 JavaEE

2022-09-19 约 1972 字 阅读时长4 分钟

Java参数校验

JSR 380

JSR 380规范是bean验证的Java api规范,JavaEE和JavaSE的一部分,使用注解如@NotNull, @Min, and @Max,确保bean属性符合一定条件

jsr380需要Java8或以上版本,利用Java8中新增的特性,如注解类型,支持新的类如:Optional 和 LocalDate

Hibernate Validator是验证规范的参考实现,使用时需要导入该依赖

@Validation与@Valid区别

来源

  • @Validated:是Spring 做得一个自定义注解,做了增强功能

  • @Valid:是JSR-303规范标准注解支持,是一个标记注解

注解位置

  • @Validated:用在类型、方法和方法参数上。但不能用于成员属性(field)

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

分组校验

  • @Validated:提供分组功能,可以在参数验证时,根据不同的分组采用不同的验证机制
  • @Valid:没有分组功能

嵌套校验

  • @Validated:不支持嵌套校验
  • @Valid:支持

总结

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

快速失败

默认会校验完所有字段,然后才抛出异常。可以通过一些简单的配置,开启Fali Fast模式,一旦校验失败就立即返回

java
1@Bean
2public Validator validator() {
3    return Validation.byProvider(HibernateValidator.class)
4        .configure()
5        .failFast(true) //关闭快速失败
6        .buildValidatorFactory().getValidator();
7}

统一异常处理

java
 1@RestControllerAdvice
 2public class CommonExceptionHandler {
 3
 4    @ExceptionHandler({MethodArgumentNotValidException.class})
 5    @ResponseStatus(HttpStatus.OK)
 6    @ResponseBody
 7    public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
 8        BindingResult bindingResult = ex.getBindingResult();
 9        StringBuilder sb = new StringBuilder("校验失败:");
10        for (FieldError fieldError : bindingResult.getFieldErrors()) {
11            sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(", ");
12        }
13        String msg = sb.toString();
14        return msg;
15    }
16
17    @ExceptionHandler({ConstraintViolationException.class})
18    @ResponseStatus(HttpStatus.OK)
19    @ResponseBody
20    public Object handleConstraintViolationException(ConstraintViolationException ex) {
21        return ex.getMessage();
22    }
23}

使用案例

导入依赖

xml
 1<!--数据校验api接口-->
 2<dependency>
 3    <groupId>Javax.validation</groupId>
 4    <artifactId>validation-api</artifactId>
 5</dependency>
 6<!--数据校验api实现 hibernate-validator只不过是一堆数据校验接口的实现类,和数据库没有半点关系-->
 7<dependency>
 8    <groupId>org.hibernate.validator</groupId>
 9    <artifactId>hibernate-validator</artifactId>
10</dependency>

基础校验

实体类

java
1@Data
2public class User {
3    @NotBlank(message = "不能为空或空串")
4    private String name;
5
6    @Max(value = 60,message = "年龄不能大于60")
7    @NotNull(message = "不能为null")
8    private Integer age;
9}

controller

java
1@RequestMapping("/validatedTest_One")
2public void validatedTest_One(@Validated User user) {
3    System.out.println("---------");
4}

此时使用 @Validated 或者 @Valid 注解都可

分组校验

实体类

java
 1@Data
 2public class User {
 3    @NotBlank(message = "不能为空或空串",groups = GroupOne.class)
 4    private String name;
 5
 6    @Min(value = 10,message = "年龄不能小于10")
 7    @NotNull(message = "不能为null")
 8    private Integer age;
 9
10    public interface GroupOne {
11
12    }
13
14    public interface GroupTwo{
15
16    }
17}

controller

java
1@RequestMapping("/validatedTest_One")
2public void validatedTest_One(@Validated({User.GroupOne.class}) User user) {
3    System.out.println("---------");
4}

@Valid不支持分组校验,@Validated支持分组校验;未指定分组接口的字段,默认为Default分组;分组接口可以实现Default接口,这时使用@Validated指定实现了Default分组的接口时,会校验Default分组的字段

嵌套校验

实体类

java
 1@Data
 2public class User {
 3    @NotBlank(message = "不能为空或空串",groups = GroupOne.class)
 4    private String name;
 5
 6    @Min(value = 10,message = "年龄不能小于10")
 7    @NotNull(message = "不能为null")
 8    private Integer age;
 9
10    @Valid
11    @NotNull()
12    private A a;
13
14    @Data
15    public class A{
16        @NotBlank(message = "不能为空")
17        private String field1;
18        private String field2;
19    }
20
21    public interface GroupOne {}
22
23    public interface GroupTwo{}
24}

controller

java
1@RequestMapping("/validatedTest_Two")
2public void validatedTest_Two(@RequestBody @Valid User user) {
3    System.out.println("---------");
4}

嵌套校验,需要校验的实体类字段必须使用@Valid注解,controller层可以使用@Valid或者 @Valid;当实体类的字段的对象为null时,默认会校验通过

集合校验

如果请求体直接传递了json数组给后台,并希望对数组中的每一项都进行参数校验。此时,如果直接使用Java.util.Collection下的list或者set来接收数据,参数校验并不会生效!可以使用自定义list集合来接收参数

编程式校验

除了通过controller接收参数时,使用注解校验,还可以通过编程式手动触发校验

需要注入一个Javax.validation.Validator的实现

java
1@Resource
2private Validator validator;
3
4@RequestMapping("/validatedTest_Three")
5public void validatedTest_Three(@RequestBody User user) {
6    //validate 为校验结果,如果无校验失败信息为空
7    Set<ConstraintViolation<User>> validate = validator.validate(user);
8}

检验普通参数

在类名上是使用@Validated注解,此时方法入参可以使用注解校验简单入参

java
1@RestController
2@RequestMapping("/test")
3@Validated
4public class TestController {
5    @RequestMapping("/validatedTest_Four")
6    public void validatedTest_Four(@NotBlank String id) {
7        System.out.println(id);
8    }
9}

普通项目中使用验证

实体类

java
 1@Data
 2@Accessors(chain = true)
 3public class UserDto {
 4
 5    @NotBlank(message = "必填项")
 6    private String id;
 7
 8    private String name;
 9
10    @NotNull
11    @Max(value = 20,message = "最大为20岁")
12    private Integer age;
13}

Demo

java
 1public class Demo {
 2    public static Validator validator= null;
 3
 4    static {
 5        validator= Validation.byProvider(HibernateValidator.class)
 6                .configure()
 7                .failFast(false) //关闭快速失败
 8                .messageInterpolator(new ParameterMessageInterpolator())
 9                .buildValidatorFactory().getValidator();
10    }
11
12    public static void main(String[] args) {
13        UserDto userDto=new UserDto();
14        userDto.setId(null)
15                .setName("lei")
16                .setAge(22);
17        Set<ConstraintViolation<UserDto>> violations = validator.validate(userDto);
18        if (!violations.isEmpty()) {
19            StringBuilder stringBuilder=new StringBuilder();
20            violations.forEach(e->stringBuilder.append(e.getPropertyPath().toString()+e.getMessage()));
21            throw new RuntimeException(stringBuilder.toString());
22        }
23    }
24}
使用滚轮缩放
按住拖动