0%

参数校验

参数校验

参数校验

客户端校验和服务端校验

客户端校验

客户端校验:提示用户正确输入,密码的输入等准确性来加强用户体验。

服务端校验

服务端校验即接口校验:更多的是为了接口安全,数据库安全,防止非法用户。

java规范:JSR-303

为什么对接口参数校验

  • 数据库汇中字段对应的数据,应该遵循业务要求和数据库设计

  • 防止(减小)范围外的输入的数据入库后影响业务正常运行的风险

  • 数据来源是接口,从入口处(接口)进行校验。

如何进行接口参数校验实现

使用@Valid

spring基于规范的实现:@Valid,对@Valid的扩展:@Validated

注解 springboot中@Validated @Valid,通过注解的方法,在实体上加注解。

  1. 非实体校验

    形参校验注解(@NotBlank、@NotNull等)

    1
    public Result delete(@RequestParam @NotBlank(message = "主键不能为空") String id){}
  2. 实体校验

    @Validated + 属性校验注解(@NotBlank、@NotNull等)

    1
    public Result update(@RequestBody @Validated UserUpdateVO data) {}
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Data
    public class UserUpdateVO {

    @NotBlank(message = "id不能为空")
    private String id;

    @NotNull(message = "小数位数不能为空")
    @Max(value = 2,message = "小数位数格式错误")
    @Min(value = 0, message = "小数位数格式错误")
    private Integer decimalDigits;

    }
  3. List校验

    @Valid + ValidList

    @Valid 起嵌套校验的作用

    原Controller方法:

    1
    public Result add(@RequestBody List<实体> data) {}

    List是jdk的接口。@Valid,@Validated 无法校验非实体Bean.

    校验List<实体>的方法。 定义ValidList 实现List接口。List<实体> → ValidList<实体>

    1
    public Result add( @RequestBody @Valid ValidList<实体> data) {}
    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
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    public class ValidList<E> implements List<E> {

    @Valid
    private List<E> list;

    public ValidList() {
    this.list = new ArrayList<>();
    }

    public ValidList(List<E> list) {
    this.list = list;
    }

    public List<E> getList() {
    return list;
    }

    public void setList(List<E> list) {
    this.list = list;
    }

    @Override
    public int size() {
    return list.size();
    }

    @Override
    public boolean isEmpty() {
    return list.isEmpty();
    }

    @Override
    public boolean contains(Object o) {
    return list.contains(o);
    }

    @Override
    public Iterator<E> iterator() {
    return list.iterator();
    }

    @Override
    public Object[] toArray() {
    return list.toArray();
    }

    @Override
    public <T> T[] toArray(T[] a) {
    return list.toArray(a);
    }

    @Override
    public boolean add(E e) {
    return list.add(e);
    }

    @Override
    public boolean remove(Object o) {
    return list.remove(o);
    }

    @Override
    public boolean containsAll(Collection<?> c) {
    return list.containsAll(c);
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
    return list.addAll(c);
    }

    @Override
    public boolean addAll(int index, Collection<? extends E> c) {
    return list.addAll(index, c);
    }

    @Override
    public boolean removeAll(Collection<?> c) {
    return list.removeAll(c);
    }

    @Override
    public boolean retainAll(Collection<?> c) {
    return list.retainAll(c);
    }

    @Override
    public void clear() {
    list.clear();
    }

    @Override
    public E get(int index) {
    return list.get(index);
    }

    @Override
    public E set(int index, E element) {
    return list.set(index, element);
    }

    @Override
    public void add(int index, E element) {
    list.add(index, element);
    }

    @Override
    public E remove(int index) {
    return list.remove(index);
    }

    @Override
    public int indexOf(Object o) {
    return list.indexOf(o);
    }

    @Override
    public int lastIndexOf(Object o) {
    return list.lastIndexOf(o);
    }

    @Override
    public ListIterator<E> listIterator() {
    return list.listIterator();
    }

    @Override
    public ListIterator<E> listIterator(int index) {
    return list.listIterator(index);
    }

    @Override
    public List<E> subList(int fromIndex, int toIndex) {
    return list.subList(fromIndex, toIndex);
    }
    }
  4. 实体内List校验

    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
    public Result update(@RequestBody @Validated UserUpdateVO data) {}

    @Data
    public class UserUpdateVO {

    @NotBlank(message = "id不能为空")
    private String id;

    @NotNull(message = "小数位数不能为空")
    @Max(value = 2,message = "小数位数格式错误")
    @Min(value = 0, message = "小数位数格式错误")
    private Integer decimalDigits;

    @Size(min = 1,message = "角色不能为空")
    @Valid
    // @Valid 开启递归校验Role
    private List<Role> roles;

    }

    @Data
    public class Role{

    @NotBlank(message = "角色不能为空")
    private String roleId;

    }


通过工具类

RegexUtils.java

将常用的校验方法放在一个类中,然后哪个参数需要校验,就调用工具类中的方法。

校验失败处理

使用全局异常捕获。 使用 @ControllerAdvice 分别定义方法,捕获并处理MethodArgumentNotValidException、BindException、ConstraintViolationException

实现

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.internal.engine.path.PathImpl;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
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 javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Set;

/**
* 参数异常处理
*/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

@ResponseBody
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public Result handleValidException(MethodArgumentNotValidException e) {
BindingResult bindingResult = e.getBindingResult();
String message = null;
if (bindingResult.hasErrors()) {
FieldError fieldError = bindingResult.getFieldError();
if (fieldError != null) {
message = fieldError.getField() + fieldError.getDefaultMessage();
}
}
return Result.validateFailed(message);
}

@ResponseBody
@ExceptionHandler(value = BindException.class)
public Result handleValidException(BindException e) {
BindingResult bindingResult = e.getBindingResult();
String message = null;
if (bindingResult.hasErrors()) {
FieldError fieldError = bindingResult.getFieldError();
if (fieldError != null) {
message = fieldError.getField() + fieldError.getDefaultMessage();
}
}
return Result.validateFailed(message);
}

@ResponseBody
@ExceptionHandler(value = ConstraintViolationException.class)
public Result handleValidException(ConstraintViolationException e) {
e.printStackTrace();
log.info("全局异常捕获:{}", e.getStackTrace());
Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
for (ConstraintViolation<?> constraintViolation : constraintViolations) {
PathImpl pathImpl = (PathImpl) constraintViolation.getPropertyPath();
// 读取参数字段,constraintViolation.getMessage() 读取验证注解中的message值
String paramName = pathImpl.getLeafNode().getName();
String message = constraintViolation.getMessage();
return Result.validateFailed(message);
}
return Result.validateFailed(e.getMessage());
}
}

常用校验注解

空校验 针对类型 说明
@Null 任何类型 校验对象必须为空
@NotNull 任何类型 校验对象不为空,不能校验字符串长度为0的对象
@NotBlank 字符串 只对字符串有效,校验字符串去掉前后空格后长度不为0
@NotEmpty 字符串、集合、数组 校验对象不能为空 (字符串长度不为0、集合大小不为0)
boolean校验 针对类型 说明
@AssertTrue 布尔 校验boolean类型必须为true
@AssertFalse 布尔 校验boolean类型必须为false
日期校验 针对类型 说明
@Past 日期类型 校验必须是一个过去的日期
@Future 日期类型 校验必须是一个将来的日期
数值校验 针对类型 说明
@Min 数字类型 校验必须是一个数字,其值必须大于或等于指定的最小值
@Max 数字类型 校验必须是一个数字,其值必须小于或等于指定的最大值
@DecimalMin 数字类型 校验必须是一个数字,其值必须大于或等于指定的最小值
@DecimalMax 数字类型 校验必须是一个数字,其值必须小于或等于指定的最大值
@Digits(integer=,fraction=) 数字类型 校验必须是一个数字,interger指定整数精度,fraction指定小数精度
@Range(min =,max = ) 数字类型、字符串 校验对象的值在min和max区间内
@Length(min =,max = ) 字符串 校验对象的值的长度在min和max区间内
@Size(min =,max = ) 字符串、集合、数组 校验对象的值在min和max区间内,如字符长度、集合大小
其他校验 针对类型 说明
@Email 字符串 校验对象的值必须是Email类型,也可以通过regexp和flag指定自定义的email格式
@Pattern 字符串 校验对象的值必须符合指定的正则表达式
@CreditCardNumber 数字类型、字符串 校验对象的值必须是信用卡类型
@URL 字符串 校验对象的值必须是URL地址

进阶用法

  1. 自己写注解类。
  2. 约束规则“分组”(group)
  3. 校验方法的返回值
  4. 约束规则支持正则表达式
  5. 支持跨参数校验 ?

参考

https://blog.csdn.net/sj13074480550/article/details/103399503