@Valid 無(wú)法校驗(yàn)List<E>的問(wèn)題
@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類(lèi)如下:
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)的方法:
// 使用包裝類(lèi)對(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í)候
[{},{}..]要改為{“l(fā)ist”: [{},{}..]}
方法2:使用@Validated+@Valid
在controller類(lèi)上面增加@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è)類(lèi),并且讓他實(shí)現(xiàn)List接口,使這個(gè)類(lèi)即具有了JavaBean的特性,又具有了List的特性,比方法1簡(jiǎn)單優(yōu)雅很多。
只需要把List換成ValidList就可以了,還不需要多統(tǒng)一異常處理。

@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類(lèi)即可,此類(lèi)通用,可以在全部由此需求的項(xiàng)目(jdk1.8)中添加,無(wú)需改動(dòng),有ValidList類(lèi)之后只要將接口方法參數(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("==============異常開(kāi)始=============");
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是自己定一的方法返回封裝類(lèi),根據(jù)自己的項(xiàng)目自己封裝就行。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Springboot全局異常捕獲及try catch區(qū)別解析
這篇文章主要介紹了Springboot全局異常捕獲及try catch區(qū)別解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06
SpringBoot實(shí)現(xiàn)自定義條件注解的代碼示例
在Spring Boot中,條件注解是一種非常強(qiáng)大的工具,它可以根據(jù)特定的條件來(lái)選擇是否加載某個(gè)類(lèi)或某個(gè)Bean,文將介紹如何在Spring Boot中實(shí)現(xiàn)自定義條件注解,并提供一個(gè)示例代碼,需要的朋友可以參考下2023-06-06
Schedule定時(shí)任務(wù)在分布式產(chǎn)生的問(wèn)題詳解
這篇文章主要介紹了Schedule定時(shí)任務(wù)在分布式產(chǎn)生的問(wèn)題詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10
詳解Spring Boot中使用@Scheduled創(chuàng)建定時(shí)任務(wù)
本篇文章中主要介紹了Spring Boot中使用@Scheduled創(chuàng)建定時(shí)任務(wù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03
Java利用ElasticSearch實(shí)現(xiàn)自動(dòng)補(bǔ)全功能
這篇文章主要為大家詳細(xì)介紹了Java如何利用ElasticSearch實(shí)現(xiàn)跟谷歌和百度類(lèi)似的下拉補(bǔ)全提示功能,文中的示例代碼講解詳細(xì),需要的可以參考一下2023-08-08
SpringBoot+Mybatis實(shí)現(xiàn)Mapper接口與Sql綁定幾種姿勢(shì)
通常我們?cè)谑褂肕ybatis進(jìn)行開(kāi)發(fā)時(shí),會(huì)選擇xml文件來(lái)寫(xiě)對(duì)應(yīng)的sql,然后將Mapper接口與sql的xml文件建立綁定關(guān)系,然后在項(xiàng)目中調(diào)用mapper接口就可以執(zhí)行對(duì)應(yīng)的sql,感興趣的可以學(xué)習(xí)一下2021-09-09
微服務(wù)Spring Boot 整合 Redis 實(shí)現(xiàn)UV 數(shù)據(jù)統(tǒng)計(jì)的詳
這篇文章主要介紹了微服務(wù)Spring Boot 整合 Redis 實(shí)現(xiàn) UV 數(shù)據(jù)統(tǒng)計(jì),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-01-01

