@Valid 無(wú)法校驗(yàn)List<E>
原因
@Valid只能校驗(yàn)JavaBean,而List<E>不是JavaBean所以校驗(yàn)會(huì)失敗,嘗試了三種解決辦法,比較推薦方法3,其他兩種大家也可以學(xué)習(xí)一下。
方法
方法1:對(duì)List進(jìn)行Wrapper
既然List不是JavaBean,那我們就把它封裝成JavaBean,我們定義一個(gè)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; } }
同時(shí)修改一下controller對(duì)應(yīng)的方法:
// 使用包裝類對(duì)list進(jìn)行驗(yàn)證 @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(); }
這樣就可以對(duì)list進(jìn)行校驗(yàn)了
注意:
由于對(duì)list進(jìn)行了包裝,如果我們傳參的時(shí)候
[{},{}..]要改為{“list”: [{},{}..]}
方法2:使用@Validated+@Valid
在controller類上面增加@Validated注解,并且刪除方法參數(shù)中的BindingResult bindingResult(因?yàn)檫@個(gè)參數(shù)已經(jīng)沒(méi)有用了,異常統(tǒng)一有controller返回了)
然后我們運(yùn)行一下測(cè)試一下
可以看到可以對(duì)參數(shù)進(jìn)行校驗(yàn)了,但還還有一個(gè)問(wèn)題,那就是這個(gè)不是我們想要的返回格式,它c(diǎn)ontroller自己返回的格式,所以我們需要做一個(gè)統(tǒng)一異常處理,代碼如下:
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()); } }
經(jīng)過(guò)統(tǒng)一異常處理,我們這邊的返回結(jié)果就是我們想要的格式了
方法3:自定義一個(gè)List
先上代碼后說(shuō)明,先定義一個(gè)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(); } ... }
對(duì)比方法3和方法1,有沒(méi)有覺(jué)得代碼有點(diǎn)相似,新建一個(gè)類,并且讓他實(shí)現(xiàn)List接口,使這個(gè)類即具有了JavaBean的特性,又具有了List的特性,比方法1簡(jiǎn)單優(yōu)雅很多。
只需要把List換成ValidList就可以了,還不需要多統(tǒng)一異常處理。
參考:https://stackoverflow.com/questions/28150405/validation-of-a-list-of-objects-in-spring/36313615#36313615
@validated 驗(yàn)證List
經(jīng)常遇到一些參數(shù)需要驗(yàn)證,用@validated 的分組驗(yàn)證方式很棒,可以解決大量的冗余代碼,提升美觀性。
但是我們平時(shí)會(huì)遇到入?yún)閘ist的校驗(yàn),@validated 的分組驗(yàn)證就無(wú)法使用了,非常麻煩,各大搜索引擎一查,總結(jié)一下比較推薦的方法:
反面例子
@***Mapping("/**") public *** apiName(@RequestBody @Validated(Add.class) List<AClass> aObject)
正常情況下這個(gè)例子是無(wú)法使用的,不是接口無(wú)法使用,是@Validated無(wú)效。這是因?yàn)槟愕娜雲(yún)?shí)體是List,其內(nèi)并沒(méi)有調(diào)用AClass的@Valid,導(dǎo)致你的校驗(yàn)規(guī)則只校驗(yàn)的List本身,并不校驗(yàn)其內(nèi)部實(shí)體。
解決方法
在項(xiàng)目里添加一個(gè)ValidList類即可,此類通用,可以在全部由此需求的項(xiàng)目(jdk1.8)中添加,無(wú)需改動(dòng),有ValidList類之后只要將接口方法參數(shù)中的List改成ValidList即可:
@***Mapping("/**") public *** apiName(@RequestBody @Validated(Add.class) ValidList<Bean> aObject)
上代碼:
注意這個(gè)構(gòu)造方法,網(wǎng)上的例子就少一點(diǎn)東西,會(huì)讓你很難用,我優(yōu)化了一下。
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); } }
這樣前端的代碼是不需要任何改動(dòng)的!
這個(gè)時(shí)候有小伙伴說(shuō),需要捕獲異常,是的,@Validated是通過(guò)拋出異常來(lái)進(jìn)行返回的。
上代碼,全局異常處理:
/** * @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()); /** * 參數(shù)為空異常處理 * * @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("參數(shù)非法:" + ((MissingServletRequestParameterException) ex).getParameterName()); } else if (ex instanceof HttpMessageNotReadableException) { //排除入?yún)?wèn)題 returnResult.setRetMsg("參數(shù)非法:" + ex.getMessage()); } else if (ex instanceof MethodArgumentNotValidException) { //排除入?yún)?wèn)題 FieldError error = (FieldError) ((MethodArgumentNotValidException) ex).getBindingResult().getAllErrors().get(0); returnResult.setRetMsg("參數(shù)非法:" + error.getField() + " " + error.getDefaultMessage()); } else if (ex instanceof ConstraintViolationException) { //排除入?yún)?wèn)題 ConstraintViolation<?> violation = ((ConstraintViolationException) ex).getConstraintViolations().iterator().next(); returnResult.setRetMsg("參數(shù)非法:" + 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("==============異常結(jié)束============="); returnResult.setRetCode(1); returnResult.setRetMsg("GlobalException!!!"); returnResult.setRetData(ex.toString()); return returnResult; } }
ReturnResult是自己定一的方法返回封裝類,根據(jù)自己的項(xiàng)目自己封裝就行。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持服務(wù)器之家。
原文鏈接:https://blog.csdn.net/weiyanqiang2012/article/details/94388824