开发过程中,参数的校验是必不可少的。前端的校验是为了用户体验,后端的校验是为了安全。试想一下,如果在controller层中没有经过任何校验的参数通过service层,dao层一路传到了数据库就可能导致严重的后果,或许查不出数据,若包含了恶意的代码,那就可能导致更严重的后果。

      还记得在最初接触Hibernate时,感慨框架功能的强大,其中就有通过配置文件便可以实现自定义参数的校验,那么功能强大的Spring是不是也可以呢?答案是肯定的。

      Hibernate Validator:

            https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#preface

      Spring  Validator:

            https://docs.spring.io/spring/docs/5.0.5.RELEASE/spring-framework-reference/core.html#validation

     首先介绍下在springBoot中的几种参数校验方式。常用于参数校验的注解如下:

  • @AssertFalse 所注解的元素必须是Boolean类型,且值为false
  • @AssertTrue 所注解的元素必须是Boolean类型,且值为true
  • @DecimalMax 所注解的元素必须是数字,且值小于等于给定的值
  • @DecimalMin 所注解的元素必须是数字,且值大于等于给定的值
  • @Digits 所注解的元素必须是数字,且值必须是指定的位数
  • @Future 所注解的元素必须是将来某个日期
  • @Max 所注解的元素必须是数字,且值小于等于给定的值
  • @Min 所注解的元素必须是数字,且值小于等于给定的值
  • @Range 所注解的元素需在指定范围区间内
  • @NotNull 所注解的元素值不能为null
  • @NotBlank 所注解的元素值有内容
  • @Null 所注解的元素值为null
  • @Past 所注解的元素必须是某个过去的日期
  • @PastOrPresent 所注解的元素必须是过去某个或现在日期
  • @Pattern 所注解的元素必须满足给定的正则表达式
  • @Size 所注解的元素必须是String、集合或数组,且长度大小需保证在给定范围之内
  • @Email 所注解的元素需满足Email格式

@NotNull  @NotEmpty  @NotBlank 3个注解的区别:

@NotNull           任何对象的value不能为null

@NotEmpty       集合对象的元素不为0,即集合不为空,也可以用于字符串不为null

@NotBlank        只能用于字符串不为null,并且字符串trim()以后length要大于0

示例:

1.引入 spring-boot-starter-validation依赖

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

2.controller中开启参数校验

2.1单个参数校验

@RestController
@Validated
public class TestController {

    @GetMapping("/getUser")
    public String getUserStr(@NotNull(message = "name 不能为空") String name,
                             @Max(value = 99, message = "不能大于99岁") Integer age) {
        return "name: " + name + " ,age:" + age;
    }
}

当处理get请求时或只传入少量参数的时候,我们可能不会建一个bean来接收这些参数,就可以像上面这样直接在controller方法的参数中进行校验。

注意:在方法所在的controller类上加入@Validated注解,不然没有任何效果。

2.2实体类参数校验

@Data
public class UserInfo {
    @NotNull(message = "username cannot be null")
    private String name;

    @NotNull(message = "sex cannot be null")
    private String sex;

    @Max(value = 99L)
    private Integer age;
}

然后在controller方法中用@RequestBody 表示这个参数接收的类:

@RestController
public class PingController {
    @Autowired
    private Validator validator;

    @GetMapping("metrics/ping")
    public Response<String> ping() {
        return new Response<>(ResponseCode.SUCCESS, null,"pang");
    }

    @PostMapping("/getUser")
    public String getUserStr(@RequestBody @Validated({GroupA.class, Default.class}) UserInfo user, BindingResult bindingResult) {
        validData(bindingResult);

        return "name: " + user.getName() + ", age:" + user.getAge();
    }

    private void validData(BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            StringBuffer sb = new StringBuffer();
            for (ObjectError error : bindingResult.getAllErrors()) {
                sb.append(error.getDefaultMessage());
            }
            throw new ValidationException(sb.toString());
        }
    }
}

需要注意的是,想让UserInfo中的参数注解生效,必须在controller参数汇总使用@Validated注解。

参数校验分组

       在实际开发中经常会用到这种情况:想要一个实体类去接收多个controller的参数,但是不同的controller所需要的参数又有些不同,而我们又不想为这点不同去建一个新的类接收参数。比如有一个/setUser接口不需要id参数,而 /getUser接口又需要该参数,这种时候就可以使用参数分组来实现。

1.定义标识分组别的interface;

public interface GroupA {
}

2.在@Validated中指定使用哪个组

@RestController
public class PingController {
    @PostMapping("/getUser")
    public String getUserStr(@RequestBody @Validated({GroupA.class, Default.class}) UserInfo user, BindingResult bindingResult) {
        validData(bindingResult);
        return "name: " + user.getName() + ", age:" + user.getAge();
    }

    @PostMapping("/setUser")
    public String setUser(@RequestBody @Validated UserInfo user, BindingResult bindingResult) {
        validData(bindingResult);
        return "name: " + user.getName() + ", age:" + user.getAge();
    }

其中Default为javax.validation.groups中的类,表示参数类中其他没有分组的参数。如果没有,/getUser接口的参数校验就只会有标记了GroupA的参数校验生效。

3.在实体类的注解中标记哪个组所使用的参数

@Data
public class UserInfo {
    @NotNull( groups = {GroupA.class}, message = "id cannot be null")
    private Integer id;

    @NotNull(message = "username cannot be null")
    private String name;

    @NotNull(message = "sex cannot be null")
    private String sex;

    @Max(value = 99L)
    private Integer age;
}

        另外还有级联参数校验,就是当参数bean中的属性又是一个复杂数据类型或者一个集合的时候,需要对其进行进一步的校验。还可以进行自定义的参数校验。这里就不一一进行阐述了。纸上得来终觉浅,绝知此事要躬行。重在实践。希望对看到的小伙伴有所帮助。