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

网友投稿 897 2022-11-30

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

@Valid 无法校验List&lt;E&gt;的问题

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

@Valid 无法校验List

原因

@Valid只能校验javaBean,而List不是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 {

@Valid

private List list;

public ListWrapper() {

list = new ArrayList<>();

}

public ListWrapper(List list) {

this.list = list;

}

}

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

// 使用包装类对list进行验证

@PostMapping("/insert/all")

public ServerResponse insertList(@Valid @RequestBody ListWrapper 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 handle(ConstraintViolationException exception) {

log.error(String.valueOf(exception));

Set> 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 implements List {

@Valid

private List list;

public ValidList() {

this.list = new ArrayList<>();

}

public ValidList(List list) {

this.list = list;

}

public List getList() {

return list;

}

public void setList(List 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 aObject)

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

解决方法

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

@***Mapping("/**")

public *** apiName(@RequestBody @Validated(Add.class) ValidList 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 implements List {

@Valid

private List list = new LinkedList<>();

public ValidList(List 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 iterator() {

return list.iterator();

}

@Override

public Object[] toArray() {

return list.toArray();

}

@Override

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

}

http:// @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 listIterator() {

return list.listIterator();

}

@Override

public ListIterator listIterator(int index) {

return list.listIterator(index);

}

@Override

public List 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是自己定一的方法返回封装类,根据自己的项目自己封装就行。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Prometheus 监控 CoreDNS
下一篇:Jenkins Docker静态agent节点的构建
相关文章

 发表评论

暂时没有评论,来抢沙发吧~