java参数校验使用

前言

在日常业务中参数校验是不可或缺的一部分,我们可以在业务代码中进行参数的规则校验(优雅的使用断言Assert),也可以在接收参数的时候直接进行校验。由代码的执行入口顺序来看,我们最好是在入参的时候进行简单校验。


一、导入依赖

springboot版本低于2.3.x,则spring-boot-starter-web则会自动引入hibernate-validator相关依赖,否则我们自己手动导入即可:

		<dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.0.1.Final</version>
        </dependency>

二、常用校验注解

@NotBlank:用于校验String类型,使得字符串不为null并且长度大于零,即.trim()的长度大于零

@NotEmpty:一般用于校验集合不为空

@NotNull:所校验的值不能为null

@Null:被检验的对象要为null

@AssertTrue:被校验的对象必须为true(我测试的时候怎么填都是false,不知道怎么测试。。。)

@AssertFalse:被校验的对象必须为false

@Min(value = val):被校验的对象必须是数字,而且大于等于val

@Max(value = val):被校验的对象必须是数字,而且小于等于val

@DecimalMin(value = "val"):同@Min(value)

@DecimalMax(value = "val"):同@Max(value)

@Size(min = min, max = max):验证注解的元素值的在minmax(包含)指定区间之内,如字符长度、集合大小(对于集合来说,null和空字符串都是算长度的)

@Past:被校验的对象(日期类型)比当前时间早

@Future:被校验的对象(日期类型)比当前时间晚

@Length(min=min, max=max):被校验字符串长度在minmax区间内,包含边界

@Range(min=min, max=max):验证注解的元素值在最小值和最大值之内,包含边界,如数字类型

@Email:验证邮箱,也可以通过regexp自定义正则匹配

@Pattern(regexp = ""):自定义正则匹配

三、@Validated和@Valid注意点

@Validated:

  1. 可以用在类型、方法和方法参数上,但是不能用在成员属性(字段)上
  2. 不能对嵌套对象进行校验
  3. 可以进行分组校验

@Valid:

  1. 可以用在方法、方法参数、构造函数、方法参数和成员属性(字段)上
  2. 可以进行嵌套,但是得在需要嵌套的字段上面加上注解
  3. 不能进行分组校验

所以大多情况下可以进行混合使用

四、实战以及问题

先写一下全局异常的处理

	/**
     * 方法参数校验
     */
    @ResponseBody
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public CommonResult<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        log.error("MethodArgumentNotValidException=======>{}", e.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(",")));
        return CommonResult.failed(e.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(",")));
    }

    /**
     * ConstraintViolationException
     */
    @ResponseBody
    @ExceptionHandler(ConstraintViolationException.class)
    public CommonResult<Object> handleConstraintViolationException(ConstraintViolationException e) {
        log.error("ConstraintViolationException=======>{}", e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(",")));
        return CommonResult.failed(e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(",")));
    }

    /**
     * ValidationException
     */
    @ResponseBody
    @ExceptionHandler(ValidationException.class)
    public CommonResult<Object> handleValidationException(ValidationException e) {
        log.error("ValidationException=======>{}", e);
        return CommonResult.failed(e.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public CommonResult<Object> serviceExceptionHandler(Exception e) {
        log.error("GlobalExceptionHandler===========>{}", e);
        return CommonResult.failed(e.getMessage());
    }

4.1 嵌套对象使用

入参:

// 入参
@Data
public class UserVo implements Serializable {
    private static final long serialVersionUID = 7965030945933727065L;

    @NotBlank(message = "姓名不能为空")
    private String name;

    @NotBlank(message = "性别不能为空")
    private String sex;
    
    @NotNull(message = "爱好不能为空")
    @Valid
    private HobbyVo hobbyVo;
}

嵌套对象:

//嵌套对象
@Data
public class HobbyVo implements Serializable {
    private static final long serialVersionUID = 7248588761842099248L;

    @NotBlank(message = "爱好名不能为空")
    private String name;
}

测试方法:

	// 测试方法 
	@PostMapping("/testSingle")
    public String test(@Validated @RequestBody UserVo userVo) {
        return "ok";
    }

结果:
不加内嵌对象,验证不通过:
在这里插入图片描述

加了内嵌对象字段,验证通过:
在这里插入图片描述

4.2 集合对象验证校验

测试方法:

	@PostMapping("/testList")
    public String test(@Validated @RequestBody List<UserVo> userVoList) {
        return "ok";
    }

结果:
在这里插入图片描述

可以看到,我们还有几个参数没填竟然还返回ok了,可见我们的校验没起作用。

解决方案

  1. 先将 Spring DataBinder 配置为使用直接字段访问
@ControllerAdvice
public class CustomControllerAdvice {

    @InitBinder
    private void activateDirectFieldAccess(DataBinder dataBinder) {
        dataBinder.initDirectFieldAccess();
    }
}
  1. 利用@Valid可以加在字段上面的特性重写List
public class ValidList<E> implements List<E> {


    @Valid
    private List<E> list = new ArrayList<>();

    @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);
    }
}
  1. 测试方法
	@PostMapping("/testList")
    public String test(@Validated @RequestBody ValidList<UserVo> userVoList) {
        return "ok";
    }
  1. 测试结果
    在这里插入图片描述

4.3分组校验

在我们平时业务中,会遇到同一个Vo进行不同的操作,但是对于Vo的检验规则是不同的,比如新增方法,对于id可为空,但是对于修改方法id不可为空,对于这种场景,就需要好好利用我们的分组校验功能了

  1. 创建分组的接口(继承javax.validation.groups.Default接口)
public interface Save extends Default {
}
public interface Update extends Default {
}
  1. 实体类
@Data
public class ProduceVo implements Serializable {
    private static final long serialVersionUID = -8921638716346141268L;

    @JsonFormat(shape = JsonFormat.Shape.STRING)
    @NotNull(groups = {Update.class}, message = "id不能为空")
    private Long id;

}
  1. 测试方法

新增

@PostMapping("/testGroupInsert")
    public String insert(@Validated(Save.class) @RequestBody ProduceVo produceVo) {
        return "ok";
    }

结果:
在这里插入图片描述
可以看到,没传参数也能成功


修改

	@PostMapping("/testGroupUpdate")
    public String update(@Validated(Update.class) @RequestBody ProduceVo produceVo) {
        return "ok";
    }

结果:
在这里插入图片描述
可以看到不传id校验不通过

4.4自定义校验

链接: 自定义参数校验注解的使用

Java中,如果想要对集合进行参数校验,可以使用自定义的集合类来包装集合,并在该包装类上添加@Valid注解。例如,可以创建一个自定义的List集合类来接收参数,并在该类上添加@Valid注解来进行参数校验。具体做法如下: ```java public class StudentList<E> implements List<E> { @Delegate @Valid public List<E> list = new ArrayList<>(); @Override public String toString() { return list.toString(); } } ``` 此外,还需要定义一个约束校验器,实现ConstraintValidator接口,并在该校验器中实现集合参数校验逻辑。例如,可以创建一个ListValueConstraintValidator类来实现校验逻辑。具体做法如下: ```java public class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> { private Set<Integer> set = new HashSet<>(); @Override public void initialize(ListValue constraintAnnotation) { int[] values = constraintAnnotation.values(); for (int val : values) { set.add(val); } } @Override public boolean isValid(Integer value, ConstraintValidatorContext context) { return set.contains(value); } } ``` 以上就是在Java中对集合进行参数校验的方法。通过自定义集合类和约束校验器,可以实现对集合中每一项的参数校验。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [java注解校验参数](https://blog.csdn.net/zqy123456_123/article/details/127109216)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值