对列表的元素进行验证

摘要:开发中经常需要校验用户提交的值是否满足要求,@Valid可用于方法参数、返回值等的验证,但是对于参数为列表时无效,此处记录几种对列表进行验证的方法

@Valid 注解通常用于验证单个对象的字段,而不是整个列表。仅添加@Valid注解不适用List原因:@Valid注解适用于Java Bean上的验证,原生的List位于Java Util, 并不属于Java Bean, 所以验证器@Valid不生效。

当需要对列表中的每个对象进行验证时,通常可使用如下途径实现:

1. 手动校验

通过手动验证,可以自己定制复杂的校验逻辑,但处理的数据多的时候工作量非常大。下例中遍历列表,对其中的元素逐个手动校验价格和库存。

java 复制代码
@ApiOperation("后台批量更新商品")
@PostMapping("/admin/product/batchUpdate")
public ApiRestResponse batchUpdateProduct(@Valid @RequestBody List<UpdateProductReq> updateProductReqList) {
    for (int i = 0; i < updateProductReqList.size() ; i++) {
        UpdateProductReq updateProductReq = updateProductReqList.get(i);
        // 方法一: 手动校验
        if (updateProductReq.getPrice() < 1) {
            throw new ImoocMallException(ImoocMallExceptionEnum.PRICE_TO_LOW);
        }
        if (updateProductReq.getStock() > 10000) {
            throw new ImoocMallException(ImoocMallExceptionEnum.STOCK_TO_MANY);
        }
        Product product = new Product();
        BeanUtils.copyProperties(updateProductReq, product);
        productService.update(product);
    }
    return ApiRestResponse.success();
}

2. 自定义列表

自定义列表自身具有将列表中属性验证的能力,例如下例中的ValidList,其仅仅是对List的接口简单封装了层。

ValidList类实现了 List 接口,并在内部包含了一个使用了@Valid注解的 List 对象。这个类的作用是具有校验能力的列表,可以对其中的元素进行验证;在使用ValidList类的时候,如果给定的元素类上有相应的校验规则,则可以通过@Valid注解来触发校验。

除了包含校验能力的列表属性外,ValidList 类还实现了List接口中的所有方法,这些方法会直接调用内部的 list 对象的对应方法来完成相应的操作。这意味着可以像使用普通列表一样使用ValidList,同时也能够享受到对元素的校验功能。 总之,ValidList 类的设计在需要对列表元素进行校验的情况下,方便地使用Bean Validation API提供的功能,同时也保留了普通列表的所有操作特性。

java 复制代码
@ApiOperation("后台批量更新商品,validList验证")
@PostMapping("/admin/product/batchUpdate2")
public ApiRestResponse batchUpdateProduct2(@Valid @RequestBody ValidList<UpdateProductReq> updateProductReqList) {
    for (int i = 0; i < updateProductReqList.size() ; i++) {
        UpdateProductReq updateProductReq = updateProductReqList.get(i);
        // 方法二: 自定义列表
        Product product = new Product();
        BeanUtils.copyProperties(updateProductReq, product);
        productService.update(product);
    }

    return ApiRestResponse.success();
}

***ValidList.java***
package com.imooc.mall.common;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import javax.validation.Valid;
import java.util.List;

/**
 * 具有检验能力的List
 */
public class ValidList<E> implements List<E> {
    @Valid
    private List<E> list;

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

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

3. 使用@Validated 注解

@Validated是Spring提供的相对于@Valid注解的功能的增强版:支持集合内元素验证;支持分组验证。

java 复制代码
// Controller中添加注解:
@Validated

@ApiOperation("后台批量更新商品,@Validated验证")
@PostMapping("/admin/product/batchUpdate3")
public ApiRestResponse batchUpdateProduct3(@Valid @RequestBody List<UpdateProductReq> updateProductReqList) {
    for (int i = 0; i < updateProductReqList.size() ; i++) {
        UpdateProductReq updateProductReq = updateProductReqList.get(i);
        // 方法三: @validated
        Product product = new Product();
        BeanUtils.copyProperties(updateProductReq, product);
        productService.update(product);
    }
    return ApiRestResponse.success();
}

使用 @Validated所对应抛出的异常需要特殊处理下,在全局异常处理类中添加;

java 复制代码
@ExceptionHandler
@ResponseBody
@ResponseStatus(HttpStatus.OK)
public ApiRestResponse handle(ConstraintViolationException exception) {
    // 从exception中获取到所有的验证违规信息,并存储在一个Set集合中
    Set<ConstraintViolation<?>> violations = exception.getConstraintViolations();
    // 创建一个StringBuilder对象,用于构建错误信息的字符串
    StringBuilder builder = new StringBuilder();
    for (ConstraintViolation<?> violation: violations) {  // 遍历违规信息的集合
        builder.append(violation.getMessage());  // 将当前违规信息的错误消息追加到builder中
        break;   // 在处理完第一条违规信息后跳出循环
    } // 传入错误码和错误消息构建一个响应对象,并将其作为方法的返回值
    return ApiRestResponse.error(ImoocMallExceptionEnum.REQUEST_PARAM_ERROR.getCode(), builder.toString());
}
相关推荐
金銀銅鐵11 小时前
带您看懂字节码指令(二):空循环里有什么
java·后端·ai编程
§ꦿCFོ༉12 小时前
嵌入式八股文—C语言入门学习(四)
c语言·开发语言·学习
投图匠13 小时前
十种不同风格的 CTP接口 框架
开发语言·python
吴名氏.13 小时前
Java转AI Agent教程(第十三周)混合检索与重排
java·数据库·人工智能·ai·ai agent
.痞老板.14 小时前
java ee--多线程(进阶)
java·java-ee
深入云栈15 小时前
Netty 4.2.x 源码深度解析 (二十三):ByteBuf 核心设计 —— 索引、类型与零拷贝
java·后端·架构
Jackzhang15 小时前
[运维排障实录] 第一次遇到 502 别急着重启:按「一条线」把问题查清楚
spring boot
爱笑的k1116 小时前
nano-vllm 源码剖析:千行 Python 手写离线推理引擎的调度、显存与并行时序
开发语言·python·vllm
wang_shu_mo_ran17 小时前
MyBatis 从入门到进阶(一)
java·spring·mybatis
X1A0RAN17 小时前
PPT 图片批量处理教程:导出全部幻灯片 · 提取全部素材
开发语言·javascript·powerpoint