react 前端框架如何驱动企业数字化转型与创新发展
989
2022-11-30
@Valid 无法校验List<E>的问题
目录@Valid 无法校验List
@Valid 无法校验List
原因
@Valid只能校验javaBean,而List
方法
方法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
public ListWrapper() {
list = new ArrayList<>();
}
public ListWrapper(List
this.list = list;
}
}
同时修改一下controller对应的方法:
// 使用包装类对list进行验证
@PostMapping("/insert/all")
public ServerResponse
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
log.error(String.valueOf(exception));
Set
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
@Valid
private List
public ValidList() {
this.list = new ArrayList<>();
}
public ValidList(List
this.list = list;
}
public List
return list;
}
public void setList(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
正常情况下这个例子是无法使用的,不是接口无法使用,是@Validated无效。这是因为你的入参实体是List,其内并没有调用AClass的@Valid,导致你的校验规则只校验的List本身,并不校验其内部实体。
解决方法
在项目里添加一个ValidList类即可,此类通用,可以在全部由此需求的项目(jdk1.8)中添加,无需改动,有ValidList类之后只要将接口方法参数中的List改成ValidList即可:
@***Mapping("/**")
public *** apiName(@RequestBody @Validated(Add.class) ValidList
上代码:
注意这个构造方法,网上的例子就少一点东西,会让你很难用,我优化了一下。
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
@Valid
private List
public ValidList(List
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
return list.iterator();
}
@Override
public Object[] toArray() {
return list.toArray();
}
@Override
public
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
return list.listIterator();
}
@Override
public ListIterator
return list.listIterator(index);
}
@Override
public List
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小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~