springboot参数校验

引入依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

测试使用类

  1. 统一返回结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    public class JsonResult {
    private boolean status;
    private int code;
    private String msg;
    private Object data;

    public JsonResult(boolean status, int code, String msg, Object data) {
    this.status = status;
    this.code = code;
    this.msg = msg;
    this.data = data;
    }

    public static JsonResult success(Object data) {
    return new JsonResult(true, 0, null, data);
    }

    public static JsonResult fail(String msg) {
    return fail(-1, msg);
    }

    public static JsonResult fail(int code, String msg) {
    return new JsonResult(false, code, msg, null);
    }

    // getter setter ...
    }
  2. 用户类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class User {

    @Length(min = 5, max = 10, message = "用户名长度不合法")
    @NotNull(message = "用户名不能为空")
    private String username;

    @Length(min = 6, max = 16, message = "密码长度不合法")
    @NotNull(message = "密码不能为空")
    private String password;

    // getter setter ...
    }

测试

  1. controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@RestController
//在请求方法中校验需要在类上加该注解
@Validated
public class UserController {

//测试post请求
//@Validated用于校验参数,如果参数校验失败,错误信息封装到BindingResult
@PostMapping("/login")
public JsonResult login(@Validated @RequestBody User user, BindingResult bindingResult){
//判断BindingResult中是否有错误信息
if(bindingResult.hasErrors()){
return JsonResult.fail(bindingResult.getFieldError().getDefaultMessage());
}
return JsonResult.success("登陆成功");
}

//测试get请求
@GetMapping("/getLogin")
public JsonResult getLogin(@Validated User user, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return JsonResult.fail(bindingResult.getFieldError().getDefaultMessage());
}
return JsonResult.success("登陆成功");
}

//测试请求方法中校验
//这种校验不支持封装到BindingResult中,需要自己做异常处理
@GetMapping("/getUser")
public JsonResult getUser(@NotNull(message = "用户名不能为空") String username){
User user = new User();
user.setUsername(username);
user.setPassword("123456");
return JsonResult.success(user);
}
}
  1. 测试

统一异常处理

  1. 异常处理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    @RestControllerAdvice
    public class GlobalExceptionHandler {

    /**
    * post请求参数校验抛出的异常
    */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public JsonResult methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e){
    //获取异常中随机一个异常信息
    String defaultMessage = e.getBindingResult().getFieldError().getDefaultMessage();
    return JsonResult.fail(defaultMessage);
    }

    /**
    * get请求参数校验抛出的异常
    */
    @ExceptionHandler(BindException.class)
    public JsonResult bindExceptionHandler(BindException e){
    //获取异常中随机一个异常信息
    String defaultMessage = e.getBindingResult().getFieldError().getDefaultMessage();
    return JsonResult.fail(defaultMessage);
    }

    /**
    * 请求方法中校验抛出的异常
    */
    @ExceptionHandler(ConstraintViolationException.class)
    public JsonResult constraintViolationExceptionHandler(ConstraintViolationException e){
    //获取异常中第一个错误信息
    String message = e.getConstraintViolations().iterator().next().getMessage();
    return JsonResult.fail(message);
    }
    }

将所有的校验异常都在异常统一处理后,controller参数中就可以不写BindingResult

  1. controller
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    @RestController
    @Validated
    public class UserBetterController {

    @PostMapping("/login")
    public JsonResult login(@Validated @RequestBody User user){
    return JsonResult.success("登陆成功");
    }

    @GetMapping("/getLogin")
    public JsonResult getLogin(@Validated User user){
    return JsonResult.success("登陆成功");
    }

    @GetMapping("/getUser")
    public JsonResult getUser(@NotNull(message = "用户名不能为空") String username){
    User user = new User();
    user.setUsername(username);
    user.setPassword("123456");
    return JsonResult.success(user);
    }
    }

分组校验

  1. 创建用于分组的接口

    1
    2
    3
    4
    5
    //校验添加角色校验
    public interface Add {}

    //校验修改角色校验
    public interface Update {}
  2. 创建一个测试分组的实体类

1
2
3
4
5
6
7
8
9
10
11
12
public class Role {

//修改角色时,必须要有id
@NotNull(message = "修改角色必须有id", groups = Update.class)
private Long id;
//添加角色时必须要有name
@NotNull(message = "添加角色必须有name", groups = Add.class)
//添加修改都需要name的长度在1-10
@Length(min = 1, max = 10, message = "名称不合法", groups = {Add.class, Update.class})
private String name;
// getter setter ...
}
  1. 创建测试controller
1
2
3
4
5
6
7
8
9
10
11
12
13
@RestController
public class RoleController {

@GetMapping("add")
public JsonResult add(@Validated(Add.class) Role role){
return JsonResult.success("添加成功");
}

@GetMapping("update")
public JsonResult update(@Validated(Update.class) Role role){
return JsonResult.success("修改成功");
}
}
  1. 测试
  • 测试添加角色
    请求:get http://localhost:8080/add
    返回结果:{“status”:false,”code”:400,”msg”:”添加角色必须有name”,”data”:null}

  • 测试修改角色
    请求:get http://localhost:8080/update
    返回结果:{“status”:false,”code”:400,”msg”:”修改角色必须有id”,”data”:null}

自定义校验注解

  1. 创建自定义校验注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
//用于校验手机号的逻辑类
@Constraint(validatedBy = PhoneValidator.class)
public @interface Phone {
//手机号的校验格式
String regexp() default "^[1][3-9][0-9]{9}$";

//出现错误返回的信息
String message() default "手机号格式错误";

Class<?>[] groups() default { };

Class<? extends Payload>[] payload() default { };

@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
public @interface List {
Phone[] value();
}
}
  1. 创建校验注解的类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//校验注解的类必须实现ConstraintValidator,第一个泛型是注解,第二个泛型是校验参数的类型(手机号是String类型)
public class PhoneValidator implements ConstraintValidator<Phone, String> {

private String regexp;

//初始化方法
@Override
public void initialize(Phone constraintAnnotation) {
//获取校验的手机号的格式
this.regexp = constraintAnnotation.regexp();
}

//value是@Phone注解所注解的字段值
//校验,返回true则通过校验,返回false则校验失败,错误信息为注解中的message
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if(value == null){
return true;
}

return value.matches(regexp);
}
}
  1. 测试
1
2
3
4
5
6
7
8
9
@RestController
@Validated
public class PhoneController {

@GetMapping("/sendPhone")
public JsonResult sendPhone(@Phone @NotNull(message = "手机号不能为空") String phone){
return JsonResult.success("正确的手机号");
}
}

请求:get http://localhost:8080/sendPhone?phone=123
返回结果:{“status”:false,”code”:400,”msg”:”手机号格式错误”,”data”:null}