使用 @Valid 注解实现各种字段的格式校验

【一】准备实体类

首先新建一个学生类

public class Student {

    /**
     * 学生姓名
     */
    private String name;

    /**
     * 学生年龄
     */
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

【二】通过普通的if-else实现字段校验

假如此时要求学生姓名不能为空且长度不能超过10,那么我们按照以前的校验方式(if-else)来写就是这样子的

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 模拟学生新增的过程
 **/
@RestController
@RequestMapping("/student")
public class StudentController {

    @GetMapping("/add")
    public String add(Student student){

        String name = student.getName();

        if(name == null || name.trim().length() == 0){
            return "学生姓名不能为空!";
        }else if (name.trim().length() > 10){
            return "学生姓名长度超过限制!";
        }
        return "添加成功";
    }
}

要求年龄字段也是非空字段且范围在1-100之间

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 模拟学生新增的过程
 **/
@RestController
@RequestMapping("/student")
public class StudentController {

    @GetMapping("/add")
    public String add(Student student){

        String name = student.getName();
        if(name == null || name.trim().length() == 0){
            return "学生姓名不能为空!";
        }else if (name.trim().length() > 10){
            return "学生姓名长度超过限制!";
        }

        Integer age = student.getAge();
        if(age == null){
            return "学生年龄不能为空!";
        }else if (age < 1 || age > 100){
            return "学生年龄有误,请重新核对!";
        }

        return "添加成功";
    }
}

通过if-else的判断来实现字段校验,可以实现需求。但是对业务代码入侵比较严重,如果此时需要校验的字段越来越多,业务代码里校验的代码就会越来越多,导致业务代码丧失可读性。

【三】使用@Valid注解实现字段校验

(1)引入依赖

在使用 @Valid 注解之前,要先引入依赖(如果是 Spring Boot 项目的话,就不需要专门引入依赖了,spring-boot-starter-web 已经帮我们引入好了)

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>1.1.0.Final</version>
</dependency>
 
<dependency>
	<groupId>org.hibernate.validator</groupId>
	<artifactId>hibernate-validator</artifactId>
</dependency>

(2)改造Controller

(1)给学生对象前增加上 @Valid 注解,就表示了我们需要对这个对象中的属性进行验证,验证的内容就是在学生类中增加的注解中的内容。
(2)BindingResult 可以理解为 @Valid 注解的“老搭档”,BindingResult 作用就是在实体类校验信息后存储校验结果。

import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

/**
 * 模拟学生新增的过程
 **/
@RestController
@RequestMapping("/student")
public class StudentController {

    @GetMapping("/add")
    public String add(@Valid Student student, BindingResult bindingResult){

        if(bindingResult.hasErrors()){
            return bindingResult.getAllErrors().get(0).getDefaultMessage();
        }
        return "添加成功";
    }
}

(3)改造实体类

package com.example.shiro.controller;

import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.Range;

import javax.validation.constraints.NotNull;

/**
 * 学生类
 * @description: Student
 * @author: 庄霸.liziye
 * @create: 2022-02-16 11:12
 **/
public class Student {

    /**
     * 学生姓名
     */
    @NotNull(message = "Valid校验:请输入学生姓名!")
    @Length(message = "名称不能超过个 {max} 字符", max = 10)
    private String name;

    /**
     * 学生年龄
     */
    @NotNull(message = "Valid校验:请输入学生年龄")
    @Range(message = "年龄范围为 {min} 到 {max} 之间", min = 1, max = 100)
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

【四】常用的校验注解

  1. @Null:被注释的元素必须为null
  2. @NotNull:被注释的元素不能为null
  3. @AssertTrue:该字段只能为true
  4. @AssertFalse:该字段的值只能为false
  5. @Min(value):被注释的元素必须是一个数字,其值必须大于等于指定的最小值
  6. @Max(value):被注释的元素必须是一个数字,其值必须小于等于指定的最大值
  7. @DecimalMin(“value”):被注释的元素必须是一个数字,验证小数的最小值
  8. @DecimalMax(“value”):被注释的元素必须是一个数字,验证小数的最大值
  9. @Size(max,min):查该字段的size是否在min和max之间,可以是字符串、数组、集合、Map等
  10. @Past:被注释的元素必须是一个过去的日期
  11. @Future:被注释的元素必须是一个将来的日期
  12. @Pattern(regexp =“[abc]”):被注释的元素必须符合指定的正则表达式。
  13. @Email:被注释的元素必须是电子邮件地址
  14. @Length(max=5,min=1,message=“长度在1~5”):检查所属的字段的长度是否在min和max之间,只能用于字符串
  15. @NotEmpty:被注释的字符串必须非空
  16. @Range:被注释的元素必须在合适的范围内
  17. @NotBlank:不能为空,检查时会将空格忽略
  18. @NotEmpty:不能为空,这里的空是指空字符串

【五】@Pattern的用法和常用的正则表达式

关于注解中需要传的参数:一般默认就填入正则表达式即可,但是java中字符串需要转义,这个需要注意一下。

常用的正则表达式:

  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 应用:计算字符串的长度(一个双字节字符长度计2ASCII字符计130 String.prototype.len=function(){return this.replace(/[^\x00-\xff]/g,"aa").length;}
 31 匹配空行的正则表达式:\n[\s| ]*\r
 32 匹配html标签的正则表达式:<(.*)>(.*)<\/(.*)>|<(.*)\/>

【六】待补充

(1)@Valid和@Validated的区别
(2)分组校验
(3)嵌套校验
(4)配合全局异常捕获

【七】@Vaild无法验证递归验证List

@NotBlank注解是用来验证字符串类型参数是否为空或者空格。然而,在List类型的参数上使用@NotBlank注解是不生效的,因为@NotBlank注解只能用在字符串类型的参数上。

@PostMapping
public String doIt(@RequestBody @Valid List<User> users) {
    return "SUCCESS";
}

User类里加@NotBlank等注解都是不会生效的

(1)方法一(实测有效)

原因是,java.util.List(ArrayList)内部通过持有一个数组来保存对象们,而作为Java官方的类,内部肯定不会在数组上声明@Valid,所以内部的对象们没有得到应有的递归校验。

所以,考虑使用一种新的java.util.List实现,来变相的达到列表校验的效果。

新增下面这个工具类

import lombok.Data;
import javax.validation.Valid;
import java.util.*;
 
/**
 * 
 * @param <E>
 * Author zkn
 */
 
@Data
public class ValidableList<E> implements List<E> {
 
  @Valid
  private List<E> list = new LinkedList<>();
 
  @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);
  }
}
 

然后把接收参数的List换成ValidableList

public ResResult saveGuardianeApp(@Valid @RequestBody ValidableList<GuardianAddReq> guardianAddReqs,
      BindingResult br) {}

(2)方法二

如果你想要验证List中的每个元素是否为空或空格,你可以使用@Valid注解结合自定义的校验注解来实现。首先,你需要创建一个自定义的校验注解,例如@NotEmptyString:

@Documented
@Constraint(validatedBy = NotEmptyStringValidator.class)
@Target({ ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@NotBlank(message = "String must not be empty or blank")
public @interface NotEmptyString {
    String message() default "String must not be empty or blank";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

然后,你需要创建一个对应的校验器类NotEmptyStringValidator来实现具体的校验逻辑,例如:

public class NotEmptyStringValidator implements ConstraintValidator<NotEmptyString, String> {
    @Override
    public void initialize(NotEmptyString constraintAnnotation) {
        // 初始化操作
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return value != null && !value.trim().isEmpty();
    }
}

接下来,你可以在ParamDictCreateAndUpdateRequest类中使用@NotEmptyString注解来验证参数:

public class ParamDictCreateAndUpdateRequest {
    @NotEmptyString
    private String param;
    
    // 其他字段...
}

最后,你需要在Controller方法中使用@Valid注解来触发参数校验:

@PostMapping("/your/api/path")
public void yourApiMethodName(@RequestBody @Valid List<ParamDictCreateAndUpdateRequest> requestList) {
    // 处理请求
}

这样,当你传入的List中的param字段为空或者空格时,将会触发校验失败并返回错误信息。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值