JSR303参数校验

2021/6/21 6:29:08

本文主要是介绍JSR303参数校验,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

JSR303参数校验

在进行web开发时,对于前端发送的参数,需要进行合法性验证,虽然前端也会进行验证,但是用户仍然可以使用postman等工具或者手动拼接url等方式绕过验证,因此后端也需要验证

要验证参数,可以使用JSR303校验机制,他适用于大多数验证场景

基本使用

使用JSR303校验,首先需要导入对应的starter

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

然后在实体类的字段上添加校验注解,并且可以使用注解的message属性指定错误信息

@Data
public class Person {
    @NotNull(message = "名字不能为空")
    String name;
    @NotBlank(message = "地址不能为空")
    String address;
    @Pattern(regexp = "^[1]([3-9])[0-9]{9}$", message = "请填写正确的手机号")
    String phone;
}

常用注解

常用的校验注解有以下这些,覆盖了大多数的校验场景

validator内置注解:

  • @Null:被注释的元素必须为null

  • @NotNull:被注释的元素必须不为null

  • @AssertTrue:被注释的元素必须为true

  • @AssertFalse:被注释的元素必须为false

  • @Min(value):被注释的元素必须是一个数字,其值必须大于等于指定的最小值

  • @Max(value):被注释的元素必须是一个数字,其值必须小于等于指定的最大值

  • @DecimalMin(value):被注释的元素必须是一个数字,其值必须大于等于指定的最小值

  • @DecimalMax(value):被注释的元素必须是一个数字,其值必须小于等于指定的最大值

  • @Size(max, min):被注释的元素的大小必须在指定的范围内

  • @Digits (integer, fraction):被注释的元素必须是一个数字,其值必须在可接受的范围内

  • @Past:被注释的元素必须是一个过去的日期

  • @Future:被注释的元素必须是一个将来的日期

  • @Pattern(value):被注释的元素必须符合指定的正则表达式

Hibernate Validator 附加的注解:

  • @Email:被注释的元素必须是电子邮箱地址

  • @Length:被注释的字符串的大小必须在指定的范围内

  • @NotEmpty:被注释的字符串的必须非空

  • @Range:被注释的元素必须在合适的范围内

  • @NotBlank:验证字符串非null,且长度必须大于0

@NotNull,@NotEmpty和@NotBlank

关于@NotNull@NotEmpty@NotBlank之间的区别如以下

  • @NotNull 适用于任何类型,被标注的元素必须不能为null
  • @NotEmpty适用于String类型,Map类型或者数组,不能为null,且长度必须大于0
  • @NotBlank只能用于String类型,不能为null,且调用trim()后,长度必须大于0

添加完需要的校验注解之后,在请求处理方法中的接收的请求参数上添加@Validated注解,即可开启校验功能

@RestController
public class MyController {

    @RequestMapping("/check")
    public String check(@Validated Person person) {
        return "checked";
    }
}

@Validated也可以用于校验Bean赋值,将注解添加在Bean的类名之上即可

@Component
@Validated
public class Person {
    @NotNull(message = "名字不能为空")
    String name;
    @NotBlank(message = "地址不能为空")
    String address;
    @Pattern(regexp = "^[1]([3-9])[0-9]{9}$", message = "请填写正确的手机号")
    String phone;
}

启动项目,发送请求到测试接口

发送一个带正确参数的请求,可以通过校验

在这里插入图片描述

再发送一个缺少name属性的请求,请求没有通过

在这里插入图片描述

查看后台发现被校验拦截,同时打印了错误信息

在这里插入图片描述

如果需要处理校验错误的信息,可以在被校验的参数之后添加一个BindingResult类的参数,与被校验的参数需要写在一起,然后使用BindingResult对象的方法判断是否校验错误和获取错误信息

现在我们在发送校验错误的时候将错误信息收集,然后在校验错误的时候将错误信息返回给前端

@RestController
public class MyController {

    @RequestMapping("/check")
    public String check(@Validated Person person, BindingResult result) {
        HashMap<String, String> map = new HashMap<>();
        if (result.hasErrors()) {
            result.getFieldErrors().forEach(error -> map.put(error.getField(), error.getDefaultMessage()));
            return map.toString();
        }
        return "checked";
    }
}

再次测试接口

在这里插入图片描述

统一异常处理

如果我们不想在每一个接口上都编写校验错误处理逻辑,我们可以使用统一异常处理机制,编写一个全局异常处理器,绑定BindException异常,在处理方法中处理校验错误既可

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(BindException.class)
    public String handleRunTimeException(BindException e) {
        HashMap<String, String> map = new HashMap<>();
        e.getFieldErrors().forEach(error -> map.put(error.getField(), error.getDefaultMessage()));
        return map.toString();
    }
}

关于统一异常处理的详细使用,可以参考我的这篇文章:全局异常处理

分组校验

有时候我们并不是每个接口都需要校验所有的字段,比如新增接口和更新接口需要校验的字段有可能是不同的,这时候可以使用校验分组功能

首先定义两个空的接口,并且继承Default接口

新增分组

public interface AddPerson extends Default {
}

更新分组

public interface UpdatePerson extends Default {
}

然后在校验注解的groups属性中绑定分组,这个属性是一个数组,如果有多个分组需要校验该字段可以用数组的方式表示

由于接口都继承了Default接口,在没有指定分组的情况下默认所有分组都要校验

@Data
public class Person {
    @NotNull(message = "名字不能为空", groups = {AddPerson.class, UpdatePerson.class})
    String name;
    @NotBlank(message = "地址不能为空", groups = AddPerson.class)
    String address;
    @Pattern(regexp = "^[1]([3-9])[0-9]{9}$", message = "请填写正确的手机号")
    String phone;
}

@Validated注解指定该次校验属于哪个分组,这样就只会校验该分组下的字段,注意如果这时候不指定分组,则指定了分组的字段都不会校验

@RestController
public class MyController {

    @RequestMapping("/add")
    public String addUser(@Validated(value = AddPerson.class) Person person, BindingResult result) {
        HashMap<String, String> map = new HashMap<>();
        if (result.hasErrors()) {
            result.getFieldErrors().forEach(error -> map.put(error.getField(), error.getDefaultMessage()));
            return map.toString();
        }
        return "checked";
    }

    @RequestMapping("/update")
    public String updateUser(@Validated(value = UpdatePerson.class) Person person, BindingResult result) {
        HashMap<String, String> map = new HashMap<>();
        if (result.hasErrors()) {
            result.getFieldErrors().forEach(error -> map.put(error.getField(), error.getDefaultMessage()));
            return map.toString();
        }
        return "checked";
    }
}

我们测试更新接口,不带address属性,而更新分组不需要校验这个属性,所以可以通过校验

在这里插入图片描述

然后发送一个不带name属性的请求,这个属性所有的分组都要校验,所以校验不通过

在这里插入图片描述

没有分组的phone属性也会被校验

在这里插入图片描述



这篇关于JSR303参数校验的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程