@Valid 无法校验List<E>的问题

目录
  • @Valid 无法校验List<E>
    • 原因
    • 方法
  • @validated 验证List
    • 反面例子
    • 解决方法

@Valid 无法校验List<E>

原因

@Valid只能校验JavaBean,而List<E>不是JavaBean所以校验会失败,尝试了三种解决办法,比较推荐方法3,其他两种大家也可以学习一下。

方法

方法1:对List进行Wrapper

既然List不是JavaBean,那我们就把它封装成JavaBean,我们定义一个ListWrapper类如下:

package com.wyq.firstdemo.util;
import lombok.Getter;
import lombok.Setter;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
@Setter
@Getter
public class ListWrapper<E> {
    @Valid
    private List<E> list;
    public ListWrapper() {
        list = new ArrayList<>();
    }
    public  ListWrapper(List<E> list) {
        this.list = list;
    }
}

同时修改一下controller对应的方法:

    // 使用包装类对list进行验证
    @PostMapping("/insert/all")
    public ServerResponse<String> insertList(@Valid @RequestBody ListWrapper<UserEntity> listWrapper, BindingResult bindingResult) {
        if(bindingResult.hasErrors()) {
            log.error(bindingResult.getFieldError().toString());
            return ServerResponse.createByErrorMessage(bindingResult.getFieldError().getDefaultMessage());
        }

        userService.insertList(listWrapper.getList());
        return ServerResponse.createBySuccess();
    }

这样就可以对list进行校验了

注意:

由于对list进行了包装,如果我们传参的时候

[{},{}..]要改为{“list”: [{},{}..]}

方法2:使用@Validated+@Valid

在controller类上面增加@Validated注解,并且删除方法参数中的BindingResult bindingResult(因为这个参数已经没有用了,异常统一有controller返回了)

然后我们运行一下测试一下

可以看到可以对参数进行校验了,但还还有一个问题,那就是这个不是我们想要的返回格式,它controller自己返回的格式,所以我们需要做一个统一异常处理,代码如下:

package com.wyq.firstdemo.handler;
import com.wyq.firstdemo.common.ServerResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Set;

@Slf4j
@RestControllerAdvice
public class ControllerExceptionHandler {

    @ExceptionHandler
    @ResponseBody
    public ServerResponse<String> handle(ConstraintViolationException exception) {
        log.error(String.valueOf(exception));
        Set<ConstraintViolation<?>> violations = exception.getConstraintViolations();
        StringBuilder builder = new StringBuilder();
        for (ConstraintViolation violation : violations) {
            builder.append(violation.getMessage());
            break;
        }
        return ServerResponse.createByErrorMessage(builder.toString());
    }
}

经过统一异常处理,我们这边的返回结果就是我们想要的格式了

方法3:自定义一个List

先上代码后说明,先定义一个ValidList

package com.wyq.firstdemo.util;
import javax.validation.Valid;
import java.util.*;
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();
    }
    ...
}

对比方法3和方法1,有没有觉得代码有点相似,新建一个类,并且让他实现List接口,使这个类即具有了JavaBean的特性,又具有了List的特性,比方法1简单优雅很多。

只需要把List换成ValidList就可以了,还不需要多统一异常处理。

参考:https://stackoverflow.com/questions/28150405/validation-of-a-list-of-objects-in-spring/36313615#36313615

@validated 验证List

经常遇到一些参数需要验证,用@validated 的分组验证方式很棒,可以解决大量的冗余代码,提升美观性。

但是我们平时会遇到入参为list的校验,@validated 的分组验证就无法使用了,非常麻烦,各大搜索引擎一查,总结一下比较推荐的方法:

反面例子

@***Mapping("/**")
public *** apiName(@RequestBody @Validated(Add.class) List<AClass> aObject)

正常情况下这个例子是无法使用的,不是接口无法使用,是@Validated无效。这是因为你的入参实体是List,其内并没有调用AClass的@Valid,导致你的校验规则只校验的List本身,并不校验其内部实体。

解决方法

在项目里添加一个ValidList类即可,此类通用,可以在全部由此需求的项目(jdk1.8)中添加,无需改动,有ValidList类之后只要将接口方法参数中的List改成ValidList即可:

@***Mapping("/**")
public *** apiName(@RequestBody @Validated(Add.class) ValidList<Bean> aObject)

上代码:

注意这个构造方法,网上的例子就少一点东西,会让你很难用,我优化了一下。

package com.analog.greatbolderserver.config.valid;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.Valid;
import java.util.*;

/**
 * @ClassName ValidList
 * @Description ValidList
 * @Author TY
 * @Date 2020/8/26 16:05
 * @Version 1.0
 **/
@Data
@NoArgsConstructor
public class ValidList<E> implements List<E> {
    @Valid
    private List<E> list = new LinkedList<>();
    public ValidList(List<E> paramList) {
        this.list = paramList;
    }

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

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

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

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

这样前端的代码是不需要任何改动的!

这个时候有小伙伴说,需要捕获异常,是的,@Validated是通过抛出异常来进行返回的。

上代码,全局异常处理:

/**
 * @ClassName GlobalException
 * @Description 异常处理拦截器
 * @Author TY
 * @Date 13:59$ 2019-01-30$
 * @Version 1.0
 **/
@RestControllerAdvice
public class GlobalException {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 参数为空异常处理
     *
     * @param ex ex
     * @return ReturnResult
     */
    @ExceptionHandler({
            ParamNullException.class, MethodArgumentNotValidException.class,
            ConstraintViolationException.class, HttpMessageNotReadableException.class,
            MissingServletRequestParameterException.class
    })
    public ReturnResult<?> requestMissingServletRequest(Exception ex) {
        ReturnResult<?> returnResult = new ReturnResult<>();
        returnResult.setRetCode(2);
        if (ex instanceof MissingServletRequestParameterException) {
            returnResult.setRetMsg("参数非法:" + ((MissingServletRequestParameterException) ex).getParameterName());
        } else if (ex instanceof HttpMessageNotReadableException) {
            //排除入参问题
            returnResult.setRetMsg("参数非法:" + ex.getMessage());
        } else if (ex instanceof MethodArgumentNotValidException) {
            //排除入参问题
            FieldError error = (FieldError) ((MethodArgumentNotValidException) ex).getBindingResult().getAllErrors().get(0);
            returnResult.setRetMsg("参数非法:" + error.getField() + " " + error.getDefaultMessage());
        } else if (ex instanceof ConstraintViolationException) {
            //排除入参问题
            ConstraintViolation<?> violation = ((ConstraintViolationException) ex).getConstraintViolations().iterator().next();
            returnResult.setRetMsg("参数非法:" + violation.getPropertyPath().toString().split("[.]")[1]
                    + violation.getMessage());
        }
        return returnResult;
    }

    /**
     * 其他异常
     *
     * @param request request
     * @param ex      ex
     * @return ReturnResult
     */
    @ExceptionHandler(Exception.class)
    public ReturnResult<?> resolveException(HttpServletRequest request, Exception ex) {
        ReturnResult<?> returnResult = new ReturnResult<>();

        logger.error("==============异常开始=============");
        logger.error("url: " + request.getRequestURL() + " msg: " + ex.getMessage());
        ex.printStackTrace();
        logger.info("==============异常结束=============");

        returnResult.setRetCode(1);
        returnResult.setRetMsg("GlobalException!!!");
        returnResult.setRetData(ex.toString());
        return returnResult;
    }
}

ReturnResult是自己定一的方法返回封装类,根据自己的项目自己封装就行。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Spring Boot 数据校验@Valid+统一异常处理的实现

    1.先在你需要校验的实体类上面加上所需要的注解 为了测试,我自己就简单写了.@NotNull 和 @NotBlank 不能为空 @Entity @Table(name = "User") @Data public class User implements Serializable { @Id @NotNull(message = "id不能为空") @GeneratedValue(strategy = GenerationType.IDENTITY) privat

  • SpringMVC使用@Valid注解进行数据验证的方法

    我们在做Form表单提交的时候,只在前端做数据校验是不够安全的,所以有时候我们需要在后端同样做数据的校验.好在SpringMVC在后台验证给我们提供了一个比较好的支持.那就是使用Valid接口的实现类做数据校验.在这之前我们先做一下准备的工作. 添加相关的Maven依赖 我们先把需要的jar包添加进来. <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api<

  • springboot @Valid注解对嵌套类型的校验功能

    @Valid注解可以实现数据的验证,你可以定义实体,在实体的属性上添加校验规则,而在API接收数据时添加@valid关键字,这时你的实体将会开启一个校验的功能,具体的代码如下,是最基本的应用: 实体: public class DepartmentDto { @ApiModelProperty("id") private String id; @ApiModelProperty("上级Id") private String parentId; @ApiModelPr

  • @Validated验证List集合的方法示例

    在开发时发现,@Validated只能验证单个实体类,在验证List集合时则不生效 @PostMapping(value="/test") public ApiResult getRepaymentPlan(@RequestBody @Validated List<Test> repaymentPlanVOs){ } 经过查资料得知,@valid是可以使用的,我们自定义一个ValidList类来替换List就可以达到验证的目的,话不多说,直接看代码 public class

  • @Valid 无法校验List<E>的问题

    目录 @Valid 无法校验List<E> 原因 方法 @validated 验证List 反面例子 解决方法 @Valid 无法校验List<E> 原因 @Valid只能校验JavaBean,而List<E>不是JavaBean所以校验会失败,尝试了三种解决办法,比较推荐方法3,其他两种大家也可以学习一下. 方法 方法1:对List进行Wrapper 既然List不是JavaBean,那我们就把它封装成JavaBean,我们定义一个ListWrapper类如下: pa

  • 手写一个@Valid字段校验器的示例代码

    上次给大家讲述了 Springboot 中的 @Valid 注解 和 @Validated 注解的详细用法: 详解Spring中@Valid和@Validated注解用法 当我们用上面这两个注解的时候,需要首先在对应的字段上打上规则注解,类似如下. @Data public class Employee { /** 姓名 */ @NotBlank(message = "请输入名称") @Length(message = "名称不能超过个 {max} 字符", max

  • 使用@Valid 校验嵌套对象

    目录 @Valid 校验嵌套对象 @Valid 嵌套对象验证不成功 @Valid 校验嵌套对象 参考网上的博客:ValidList 能校验list. 通过测试发现,@Valid只能校验一层.比如我这里有个person对象,里面有个ValidList<Teacher> 属性,Teacher对象里面有个List<Student> 属性. 如果在Controller层加上@Valid 是校验不到ValidList<Student> 属性的. 需要在List<Teache

  • 快速校验实体类时,@Valid,@Validated,@NotNull注解无效的解决

    目录 校验实体类参数内容不能为空时使用注解校验无效 使用@valid注解首先引入依赖 1.更换方法入参类型 2.错误内容过多 3.使用对象接收错误内容,按自己要求输出 springboot 校验机制 @Validated @Valid 1.探究原因 2.使用@Validated 实现校验机制 3.使用@Valid 实现校验机制 校验实体类参数内容不能为空时使用注解校验无效 使用@valid注解首先引入依赖 如果是SpringBoot项目,引入web开发包,就不需要再单独引入@valid依赖了.因

  • Java中的三种校验注解的使用(@Valid,@Validated和@PathVariable)

    目录 @Valid和@Validated @Valid和@Validated比较 @Valid高级使用 @Valid级联校验 @Validated高级使用 @Validated分组校验 @Validated分组校验顺序 @Validated非实体类校验 @PathVariable 正则表达式校验 继承BasicErrorController类 自定义校验注解 @Valid和@Validated @Valid和@Validated比较 相同点: @Valid注解和 @Validated注解都是开启

  • element form 校验数组每一项实例代码

    1.校验的数据结构如下: 2.html结构 <el-dialog :title="title" :visible.sync="visable" width="40%" :before-close="cancel"> <el-form label-width="80px" :model="formData" :rules="formDataRules"

  • SpringMvc @Valid如何抛出拦截异常

    SpringMvc中,校验参数可以使用 @Valid 注解,同时在相应的对象里使用 @NotBlank( message = "昵称不能为空") @NotNull( message = "ID不能为空") @Pattern( message = "不能包括空格" , regexp = "\\S+" ) 等等. 这个校验会把所有的参数都校验一遍,所以它的异常里会好些列表,直接使用e.getMessage(),会输出很多累赘的东西

  • SpringBoot2 参数管理实践之入参出参与校验的方式

    目录 一.参数管理 二.接收参数 三.响应参数 四.参数校验 1.借鉴参考 2.常用校验方式 五.源代码地址 一.参数管理 在编程系统中,为了能写出良好的代码,会根据是各种设计模式.原则.约束等去规范代码,从而提高代码的可读性.复用性.可修改,实际上个人觉得,如果写出的代码很好,即别人修改也无法破坏原作者的思路和封装,这应该是非常高水准. 但是在日常开发中,碍于很多客观因素,很少有时间去不断思考和优化代码,所以只能从实际情况的角度去思考如何构建系统代码,保证以后自己还能读懂自己的代码,在自己的几

  • Vue Element-ui表单校验规则实现

    目录 1.前言 2.规则校验的入门模式 2.1.示例代码 2.2.form项 2.3.prop项 2.4.rules项 2.5.rule项 2.6.使用规则 2.7.规则校验的核心 3.规则校验的进阶模式 3.1.嵌套对象属性名 3.2.自定义校验器validator 3.3.类型type 3.3.数据转换transform 3.4.数值范围Range 3.5.枚举值 3.6.正则Pattern 3.7.长度len 3.8.空白whitespace 3.9.i18n 4.规则校验的高级模式 4.

  • 如何使用Bean Validation 解决业务中参数校验

    前言 在开发中经常需要写一些字段校验的代码,比如字段非空,字段长度限制,邮箱格式验证等等,写这些与业务逻辑关系不大的代码个人感觉有点麻烦: 验证代码繁琐,重复劳动 方法内代码显得冗长 每次要看哪些参数验证是否完整,需要去翻阅验证逻辑代码 叙述 Bean Validation是一个通过配置注解来验证参数的框架,它包含两部分Bean Validation API和Hibernate Validator. Bean Validation API是Java定义的一个验证参数的规范. Hibernate

随机推荐