后端web-api参数校验

目录

一.基础校验

1.数字校验

(1)@DecimalMax

(2)@DecimalMin

(3)@Max

(4)@Min

(5)@Digits(integer=,fraction=)

2.字符串校验

(1)@NotBlank

3.时间校验

(1)@Future

(2)@Past

4.集合校验

(1)@NotEmpty

5.布尔值校验

(1)@AssertFalse

(2)@AssertTrue

6.其他校验

(1)@NotNull

(2)@Size

(3)@Email

(4)@Pattern(regexp=)

二.高级校验

1.自定义注解校验

2.分组校验

3.校验工具

4.集合校验

三.其他校验

1.全局异常处理


一.基础校验

1.数字校验

(1)@DecimalMax

  • 标注的元素可定义为BigDecimal、BigInteger、String、byte、short、int、long以及他们的包装类型
  • 不支持double、float类型(否则小数点会有精度丢失的可能)
  • 限制必须为数字,最大值不能超过指定值

举例:

@Data
public class DailyDTO implements Serializable {
    private static final long serialVersionUID = -513334502940807737L;
    @DecimalMax(value = "99")
    private String parameter;
}
@RestController
public class AnnotationUser {
    @PostMapping("/validator")
    public Object validator(@Valid DailyDTO dailyDTO){
        return "ok";
    }
}

输入值必须是数字并且小于等于99

  • 发送请求校验入参-99999(成功)
  • 发送请求校验入参0(成功)
  • 发送请求校验入参1(成功)
  • 发送请求校验入参99(成功)
  • 发送请求校验入参100(失败)

(2)@DecimalMin

  • 标注的元素可定义为BigDecimal、BigInteger、String、byte、short、int、long以及他们的包装类型
  • 不支持double、float类型(否则小数点会有精度丢失的可能) 
  • 限制必须为数字,最小值不能低于指定值

举例:

@Data
public class DailyDTO implements Serializable {
    private static final long serialVersionUID = -513334502940807737L;
    @DecimalMin(value = "99")
    private String parameter;
}
@RestController
public class AnnotationUser {
    @PostMapping("/validator")
    public Object validator(@Valid DailyDTO dailyDTO){
        return "ok";
    }
}

输入值必须是数字并且大于等于99

  • 发送请求校验入参99999(成功)
  • 发送请求校验入参100(成功)
  • 发送请求校验入参99(成功)
  • 发送请求校验入参89(失败)
  • 发送请求校验入参0(失败)
  • 发送请求校验入参-1(失败)

(3)@Max

  • 标注的元素可定义为BigDecimal、BigInteger、String、byte、short、int、long以及他们的包装类型
  • 不支持double、float类型(否则小数点会有精度丢失的可能) 
  • 限制必须为数字,最大值不能超过指定值

举例:

@Data
public class DailyDTO implements Serializable {
    private static final long serialVersionUID = -513334502940807737L;
    @Max(value = 10)
    private String parameter;
}
@RestController
public class AnnotationUser {
    @PostMapping("/validator")
    public Object validator(@Valid DailyDTO dailyDTO){
        return "ok";
    }
}

最大值不能超过10

  • 发送请求校验入参10(成功)
  • 发送请求校验入参9.99999999999999999999999999(成功)
  • 发送请求校验入参0(成功)
  • 发送请求校验入参-999999999999999999999(成功)
  • 发送请求校验入参10.00000000000000000000000000001(失败)

(4)@Min

  • 标注的元素可定义为BigDecimal、BigInteger、String、byte、short、int、long以及他们的包装类型
  • 不支持double、float类型(否则小数点会有精度丢失的可能) 
  • 限制必须为数字,最小值不能低于指定值

举例:

@Data
public class DailyDTO implements Serializable {
    private static final long serialVersionUID = -513334502940807737L;
    @Min(value = 10)
    private String parameter;
}
@RestController
public class AnnotationUser {
    @PostMapping("/validator")
    public Object validator(@Valid DailyDTO dailyDTO){
        return "ok";
    }
}

最小值不能小于10

  • 发送请求校验入参10(成功)
  • 发送请求校验入参10.00000000000000000000001(成功)
  • 发送请求校验入参0(失败)
  • 发送请求校验入参9.999999999999999999999(失败)
  • 发送请求校验入参-1(失败)

(5)@Digits(integer=,fraction=)

  • 标注的元素可定义为BigDecimal、BigInteger、String、byte、short、int、long以及他们的包装类型
  • 限制必须为数字类型,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction

举例:

@Data
public class DailyDTO implements Serializable {
    private static final long serialVersionUID = -513334502940807737L;
    @Digits(integer = 2,fraction = 2)
    private String parameter;
}
@RestController
public class AnnotationUser {
    @PostMapping("/validator")
    public Object validator(@Valid DailyDTO dailyDTO){
        return "ok";
    }
}

输入值必须是数字并且是2位整数和2位小数

  • 发送请求校验入参99.99(成功)
  • 发送请求校验入参-99.99(成功)
  • 发送请求校验入参0(成功)
  • 发送请求校验入参999(失败)
  • 发送请求校验入参-999(失败)
  • 发送请求校验入参99.999(失败)
  • 发送请求校验入参-99.999(失败)
  • 发送请求校验入参999.999(失败)

2.字符串校验

(1)@NotBlank

  • 入参不能为空,只支持String类型的校验

举例:

@Data
public class DailyDTO implements Serializable {
    private static final long serialVersionUID = -513334502940807737L;
    @NotBlank
    private String parameter;
}
@RestController
public class AnnotationUser {
    @PostMapping("/validator")
    public Object validator(@Valid DailyDTO dailyDTO){
        return "ok";
    }
}

入参不能为空

  • 发送请求校验入参空值(失败)
  • 发送请求校验入参其他值(成功)

3.时间校验

(1)@Future

  • 必须为未来时间

举例:

@Data
public class DailyDTO implements Serializable {
    private static final long serialVersionUID = -513334502940807737L;
    @Future
    private Date parameter;
}
@RestController
public class AnnotationUser {
    @PostMapping("/validator")
    public Object validator(@Valid DailyDTO dailyDTO){
        return "ok";
    }
}
  • 发送请求校验入参2050-01-01T12:12:12(成功)
  • 发送请求校验入参2000-01-01T12:12:12(失败)

(2)@Past

  • 必须为过去时间

举例:

@Data
public class DailyDTO implements Serializable {
    private static final long serialVersionUID = -513334502940807737L;
    @Past
    private Date parameter;
}
@RestController
public class AnnotationUser {
    @PostMapping("/validator")
    public Object validator(@Valid DailyDTO dailyDTO){
        return "ok";
    }
}
  • 发送请求校验入参2000-01-01T12:12:12(成功)
  • 发送请求校验入参2050-01-01T12:12:12(失败)

(3)@DateTimeFormat

前端传至后端规定的格式

  • @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")

(4)@JsonFormat

后端转换date类型格式传至前端

  • @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")

4.集合校验

(1)@NotEmpty

  • 入参不能为空,只支持List、Set、Map集合的校验

举例:

@Data
public class DailyDTO implements Serializable {
    private static final long serialVersionUID = -513334502940807737L;
    @NotEmpty
    private List<String> parameter;
}
@RestController
public class AnnotationUser {
    @PostMapping("/validator")
    public Object validator(@RequestBody @Valid DailyDTO dailyDTO){
        return "ok";
    }
}

入参不能为空

  • 发送请求校验入参空值(失败)
  • 发送请求校验入参其他值(成功)

5.布尔值校验

  • 注意:包装类型的Boolean没有默认值,可以检验空
  • 注意:基础类型的boolean有默认值,空即为false,无法检验空值!!! 
  • 所以阿里的Java开发规范也是建议我们使用包装类型定义

(1)@AssertFalse

  • 标注的元素必须定义为布尔类型
  • 校验必须是null值或者布尔类型的false值

举例:

@Data
public class DailyDTO implements Serializable {
    private static final long serialVersionUID = -513334502940807737L;
    @AssertFalse(message = "一定要为false")
    private Boolean parameter;
}
@RestController
public class AnnotationUser {
    @PostMapping("/validator")
    public Object validator(@Valid DailyDTO dailyDTO){
        return "ok";
    }
}

传递的参数如果不确定就传空,否则必须传false

  • 发送请求校验入参false(成功)
  • 发送请求校验入参null(成功)
  • 发送请求校验入参true(失败)

(2)@AssertTrue

  • 标注的元素必须定义为布尔类型
  • 校验必须是null值或者布尔类型的true值

举例:

@Data
public class DailyDTO implements Serializable {
    private static final long serialVersionUID = -513334502940807737L;
    @AssertTrue(message = "一定要为true")
    private Boolean parameter;
}
@RestController
public class AnnotationUser {
    @PostMapping("/validator")
    public Object validator(@Valid DailyDTO dailyDTO){
        return "ok";
    }
}

传递的参数如果不确定就传空,否则必须传true

  • 发送请求校验入参true(成功)
  • 发送请求校验入参null(成功)
  • 发送请求校验入参false(失败)

6.其他校验

(1)@NotNull

  • 入参不能为空,支持定义任何类型,多数用于基础类型的校验

举例:

@Data
public class DailyDTO implements Serializable {
    private static final long serialVersionUID = -513334502940807737L;
    @NotNull
    private Long parameter;
}
@RestController
public class AnnotationUser {
    @PostMapping("/validator")
    public Object validator(@Valid DailyDTO dailyDTO){
        return "ok";
    }
}

入参不为空即可,且符合定义的类型

  • 发送请求校验入参空值(失败)
  • 发送请求校验入参其他值(成功)

(2)@Size

  • 限制入参必须为字符串或者集合,不超过定义的范围

举例:

@Data
public class DailyDTO implements Serializable {
    private static final long serialVersionUID = -513334502940807737L;
    
    @Size(min = 1,max = 3,message = "字符串超过该范围")
    private List<String> parameter;
}
@RestController
public class AnnotationUser {
    @PostMapping("/validator")
    public Object validator(@RequestBody @Valid DailyDTO dailyDTO){
        return "ok";
    }
}

限定该集合长度范围是1-3

  • 发送请求校验入参 { "parameter": ["测试1","测试2","测试3"] }(成功)
  • 发送请求校验入参 { "parameter": ["测试1"] }(成功)
  • 发送请求校验入参 { "parameter": ["测试1","测试2","测试3","测试4"] }(失败)
  • 发送请求校验入参其他范围值(失败)

(3)@Email

  • 邮箱格式校验,必须是@后跟*.*格式拼接

举例:

@Data
public class DailyDTO implements Serializable {
    private static final long serialVersionUID = -513334502940807737L;
   
    @Email
    private String parameter;
}
@RestController
public class AnnotationUser {
    @PostMapping("/validator")
    public Object validator(@Valid DailyDTO dailyDTO){
        return "ok";
    }
}

必须为一个邮箱的格式

  • 发送请求校验入参21412421@(失败)
  • 发送请求校验入参21412421@qq.(失败)
  • 发送请求校验入参21412421@.com(失败)
  • 发送请求校验入参21412421@qq.c(成功)
  • 发送请求校验入参其他非邮箱格式(失败)

(4)@Pattern(regexp=)

  • 声明的字符串需要匹配定义的正则表达式,或者为null 值

举例:

@Data
public class DailyDTO implements Serializable {
    private static final long serialVersionUID = -513334502940807737L;
   
    @Pattern(regexp = "^[0-9]*$")
    private List<String> parameter;
}
@RestController
public class AnnotationUser {
    @PostMapping("/validator")
    public Object validator(@Valid DailyDTO dailyDTO){
        return "ok";
    }
}

该正则表达式:必须为0-9的数字

  • 发送请求校验入参45465465(成功)
  • 发送请求校验入参其他非数字(失败)

下面是常用的正则表达式(注意转义符的使用,例如:“\d”要写成“\\d”):

1 匹配首尾空格的正则表达式:(^\s*)|(\s*$)

2 整数或者小数:^[0-9]+\.{0,1}[0-9]{0,2}$

3 只能输入数字:"^[0-9]*$"。

4 只能输入n位的数字:"^\d{n}$"。

5 只能输入至少n位的数字:"^\d{n,}$"。

6 只能输入m~n位的数字:。"^\d{m,n}$"

7 只能输入零和非零开头的数字:"^(0|[1-9][0-9]*)$"。

8 只能输入有两位小数的正实数:"^[0-9]+(.[0-9]{2})?$"。

9 只能输入有1~3位小数的正实数:"^[0-9]+(.[0-9]{1,3})?$"。

10 只能输入非零的正整数:"^\+?[1-9][0-9]*$"。

11 只能输入非零的负整数:"^\-[1-9][]0-9"*$。

12 只能输入长度为3的字符:"^.{3}$"。

13 只能输入由26个英文字母组成的字符串:"^[A-Za-z]+$"。

14 只能输入由26个大写英文字母组成的字符串:"^[A-Z]+$"。

15 只能输入由26个小写英文字母组成的字符串:"^[a-z]+$"。

16 只能输入由数字和26个英文字母组成的字符串:"^[A-Za-z0-9]+$"。

17 只能输入由数字、26个英文字母或者下划线组成的字符串:"^\w+$"。

18 验证用户密码:"^[a-zA-Z]\w{5,17}$"正确格式为:以字母开头,长度在6~18之间,只能包含字符、数字和下划线。

19 验证是否含有^%&',;=?$\"等字符:"[^%&',;=?$\x22]+"。

20 只能输入汉字:"^[\u4e00-\u9fa5]{0,}$"

21 验证Email地址:"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$"。

22 验证InternetURL:"^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$"。

23 验证电话号码:"^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$"正确格式为:"XXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"和"XXXXXXXX"。

24 验证身份证号(15位或18位数字):"^\d{15}|\d{18}$"。

25 验证一年的12个月:"^(0?[1-9]|1[0-2])$"正确格式为:"01"~"09"和"1"~"12"。

26 验证一个月的31天:"^((0?[1-9])|((1|2)[0-9])|30|31)$"正确格式为;"01"~"09"和"1"~"31"。

27 匹配中文字符的正则表达式: [\u4e00-\u9fa5]

28 匹配双字节字符(包括汉字在内):[^\x00-\xff]

29 应用:计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)

30 String.prototype.len=function(){return this.replace(/[^\x00-\xff]/g,"aa").length;}

31 匹配空行的正则表达式:\n[\s| ]*\r

32 匹配html标签的正则表达式:<(.*)>(.*)<\/(.*)>|<(.*)\/>

二.高级校验

1.自定义注解校验

  • 自定义注解@DailyType进行参数校验
  • 参考框架内的注解编写
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE
})
@Retention(RUNTIME)
@Documented
// 指定具体的校验逻辑接口
@Constraint(validatedBy = {DailyTypeEnumValidator.class })
public @interface DailyType {
    String message() default "参数不合法";
    Class<?>[] groups() default { };
    Class<? extends Payload>[] payload() default { };
}
  • 实现ConstraintValidator接口,编写自定义注解的校验逻辑
  • 举例:这里校验参数必须等于"hiber",否则抛出异常
public class DailyTypeEnumValidator implements ConstraintValidator<DailyType, String> {
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        //这里进行参数校验,举例:值必须等于“hiber”,才符合通过
        if("hiber".equals(value)){
            return true;
        }
        return false;
    }
}

2.分组校验

  • 先定义两个空接口:新增和更新
public interface Insert {
}
public interface Update {
}
  • 对参数进行制定分组校验
/**
要使用分组校验的话, 需要使用  @Validated 这个注解
*/
@Data
public class PersonDTO {
  /**
  * id, 更新时候校验
  */
  @NotNull(groups = Update.class, message = "id不能为空")
  private Integer id;
  @NotBlank(groups =
     {
          Insert.class,
          Update.class
     },
      message = "名字不能为空"
 )
  private String name;
  @NotBlank(groups = Update.class, message = "性别不能为空")
  private String gender;
  /**
  * 没有指定分组, 即为默认分组 Default
  */
  @NotNull(message = "年龄不能为空")
  private Integer age;
}
  • 需使用注解@Validated进行制定分组校验
/**
  * 新增分组校验测试
  * @param personDTO
  * @return
  */
@PostMapping("/person")
public Object groupValidationInsertDemo(@Validated(Insert.class) @RequestBody
PersonDTO personDTO){
  return "ok";
}
/**
  * 更新分组校验测试
  * @param personDTO
  * @return
  */
@PutMapping("/person")
public Object groupValidationUpdateDemo(@Validated(Update.class) @RequestBody
PersonDTO personDTO){
  return "ok";
}

3.校验工具

  • 自定义编写工具,一般用于service层的校验,考虑到service层不仅仅给controller层调用
public class ValidatorUtils {

  private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
  public static <T> void validate(T object, Class... groups) {
    Set<ConstraintViolation<T>> validate =
        validator.validate(object, groups);
    // 如果校验结果不为空
    if (!CollectionUtils.isEmpty(validate)) {
      StringBuilder exceptionMessage = new StringBuilder();
      validate.forEach(constraintViolation -> {
        exceptionMessage.append(constraintViolation.getMessage());
     });
     
      throw new IllegalArgumentException(exceptionMessage.toString());
   }
 }
}

4.集合校验

  • 编写一个实体,这里的age属性没有标注,可为空
@Data
public class PersonDTO{
    private Integer age;
}
  • 使用重写的ValidatorList对实体PersonDTO进行封装,请求测试
  1. 传递参数空列表(失败)
  2. 传递参数列表中的空属性(成功)
 @PostMapping("/level")
    public Object levelValidation(@Validated @RequestBody ValidatorList<PersonDTO> personDTOList) {
        System.err.println(personDTOList.toString());
        return "ok";
    }
  • 这里重写List,在校验对象的属性上使用@Valid注解实现级联校验,列表List不能为空
@Data
public class ValidatorList<E> implements List<E> {
    @Valid
    @NotEmpty(message = "请求参数集合不能为空")
    @NotNull(message = "集合不能为NULL")
    private List<E> list = new ArrayList<E>();

    @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 boolean equals(Object o) {
        return list.equals(o);
    }

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

    @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);
    }
}

三、其他校验

1.全局异常处理

  • 在基础的参数校验中所产生的异常仅服务端可见
  • 把该异常抛出去到前端也可见
  • 添加以下的配置类(仅针对MethodArgumentNotValidException异常处理),当 @Valid 作用的参数上,校验不通过时候,抛出该异常。
  • 备注: @Validated 这个注解一样也有效
@RestControllerAdvice
@Configuration
@Slf4j
public class GlobalExceptionHandle {
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Object validationExceptionHandle(MethodArgumentNotValidException mav) {
    log.warn("----- handle argument not valid exception! -----");
    List<FieldError> fieldErrors = mav.getBindingResult().getFieldErrors();
    return fieldErrors.stream().map(item -> {
        StringJoiner sj = new StringJoiner(":");
        sj.add(item.getField());
        sj.add(item.getDefaultMessage());
        return sj.toString();
    }).collect(Collectors.toList());
    }
}
  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值