@Valid注解对List无效

@Valid注解对List无效

如果前端传入的表单中带有复选框,那后台的怎么用@Valid校验?


比如我想添加一个小组,前端传入的信息包括name小组名字,和userIdList小组成员id的列表(该字段为复选框)。

后台的请求模型如下

@Data
public class GroupAddRequest {
    @NotBlank(message = "name不能为空")
    @Size(min = 2, max = 10, message = "name长度需要在2~10之间")
    private String name;

    private List<Long> userIdList;
}

我想对userIdList中的各个成员进行校验,要求传入的userId都必须大于0

但是添加@Min和@Max注解都没用。

如何优雅的对List成员进行校验?

正在回答

登陆购买课程后可参与讨论,去登陆

10回答

同学你好,1、这里老师尝试了一下在ValidList中封装为实体类确实是比较麻烦的,并不好实现。

    ​2、其实这里对于前端传递的集合数据,更多的是在前端里直接进行验证。

    ​3、如果同学想实现在后端的list验证,同学其实可以尝试在这个list集合的set方法中,直接添加一个验证,比如:

http://img1.sycdn.imooc.com//climg/5fa3a20909cba5e905550220.jpg


http://img1.sycdn.imooc.com//climg/5fa3a65b09c3d59205790322.jpg

如上所示,也可以根据提示信息定位问题。

  • 慕丝1539783 提问者 #1
    试了一下,这个做法可以是可以,但是又有新问题, @Data public class GroupAddRequest { @NotBlank(message = "name不能为空") @Size(min = 2, max = 10, message = "name长度需要在2~10之间") private String name; @Min(value = 1, message = "adminId不能小于1") private Long adminId; private List<Long> userIdList; public void setUserIdList(List<Long> userIdList) { for (Long userId : userIdList) { if (userId < 1) throw new ManagementException(ManagementExceptionEnum.PARA_ERROR); } this.userIdList = userIdList; } } 但是前端userIdList传入小于1的数据,那返回的数据格式如下: { "status": 10003, "msg": "[Property 'userIdList' threw exception; nested exception is ManagementException(code=10003, message=参数错误)]", "data": null } msg的内容和预想的不一样,应该是@Valid把我自定义的异常给捕获了,然后再抛出BindException异常,如何改变msg的内容?
    2020-11-06 13:08:08
好帮手慕阿满 2020-11-06 17:10:43

同学你好,建议同学检查一下处理统一异常的handler,在处理ManagementException异常时,是否返回了ApiRestResponse对象,例如:

http://img1.sycdn.imooc.com//climg/5fa511e2094af98408120196.jpg

​祝:学习愉快~

慕丝1539783 提问者 2020-11-06 13:10:27

试了一下,这个做法可以是可以,但是又有新问题

@Data
public class GroupAddRequest {
@NotBlank(message = "name不能为空")
@Size(min = 2, max = 10, message = "name长度需要在2~10之间")
private String name;

@Min(value = 1, message = "adminId不能小于1")
private Long adminId;

private List<Long> userIdList;

public void setUserIdList(List<Long> userIdList) {
for (Long userId : userIdList) {
if (userId < 1) throw new ManagementException(ManagementExceptionEnum.PARA_ERROR);
}
this.userIdList = userIdList;
}
}


但是前端userIdList传入小于1的数据,那返回的数据格式如下


{
    "status": 10003,
    "msg": "[Property 'userIdList' threw exception; nested exception is ManagementException(code=10003, message=参数错误)]",
    "data": null
}

msg的内容和预想的不一样,应该是@Valid把我自定义的异常给捕获了,然后再抛出BindException异常。

如何改变msg的内容?

慕丝1539783 提问者 2020-11-04 21:10:46

controller

@RestController
@RequestMapping("/group")
public class GroupController {

    @PostMapping("/add")
    public ApiRestResponse<Object> add(@Valid GroupAddRequest groupAddRequest) {
        System.out.println(groupAddRequest);
        return ApiRestResponse.success();
    }
}

ValidList

import org.jetbrains.annotations.NotNull;

import javax.validation.Valid;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;

public class ValidList<E> implements List<E> {

    @Valid
    private List<E> list;

    public ValidList() {
        this.list = new ArrayList<E>();
    }

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

    @Override
    public boolean contains(Object o) {
        return false;
    }

    @Override
    public Iterator<E> iterator() {
        return null;
    }

    @Override
    public void forEach(Consumer<? super E> action) {

    }

    @Override
    public Object[] toArray() {
        return new Object[0];
    }

    @Override
    public <T> T[] toArray(@NotNull T[] a) {
        return null;
    }

    @Override
    public boolean add(E e) {
        return false;
    }

    @Override
    public boolean remove(Object o) {
        return false;
    }

    @Override
    public boolean containsAll(@NotNull Collection<?> c) {
        return false;
    }

    @Override
    public boolean addAll(@NotNull Collection<? extends E> c) {
        return false;
    }

    @Override
    public boolean addAll(int index, @NotNull Collection<? extends E> c) {
        return false;
    }

    @Override
    public boolean removeAll(@NotNull Collection<?> c) {
        return false;
    }

    @Override
    public boolean removeIf(Predicate<? super E> filter) {
        return false;
    }

    @Override
    public boolean retainAll(@NotNull Collection<?> c) {
        return false;
    }

    @Override
    public void replaceAll(UnaryOperator<E> operator) {

    }

    @Override
    public void sort(Comparator<? super E> c) {

    }

    @Override
    public void clear() {

    }

    @Override
    public E get(int index) {
        return null;
    }

    @Override
    public E set(int index, E element) {
        return null;
    }

    @Override
    public void add(int index, E element) {

    }

    @Override
    public E remove(int index) {
        return null;
    }

    @Override
    public int indexOf(Object o) {
        return 0;
    }

    @Override
    public int lastIndexOf(Object o) {
        return 0;
    }

    @Override
    public ListIterator<E> listIterator() {
        return null;
    }

    @Override
    public ListIterator<E> listIterator(int index) {
        return null;
    }

    @Override
    public List<E> subList(int fromIndex, int toIndex) {
        return null;
    }

    @Override
    public Spliterator<E> spliterator() {
        return null;
    }

    @Override
    public Stream<E> stream() {
        return null;
    }

    @Override
    public Stream<E> parallelStream() {
        return null;
    }
}

Admin

import lombok.Data;

import javax.validation.constraints.Min;

@Data
public class Admin {
//    @Min(value = 1, message = "userId不能小于1")
    Long userId;
}

GroupAddRequest 

import lombok.Data;
import org.hibernate.validator.constraints.NotBlank;

import javax.validation.constraints.Min;
import javax.validation.constraints.Size;

@Data
public class GroupAddRequest {
    @NotBlank(message = "name不能为空")
    @Size(min = 2, max = 10, message = "name长度需要在2~10之间")
    private String name;

    @Min(value = 1, message = "adminId不能小于1")
    private Long adminId;

    private ValidList<Admin> userIdList;
}

postman

http://img1.sycdn.imooc.com//climg/5fa257b60843641715860499.jpg

报错信息

[11:04 21:05:20.135] [ERROR] [com.fjlonge.management.filter.WebLogAspect] - Exception:org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors

Field error in object 'groupAddRequest' on field 'userIdList': rejected value [1,2,8,9]; codes [typeMismatch.groupAddRequest.userIdList,typeMismatch.userIdList,typeMismatch.com.fjlonge.management.model.request.ValidList,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [groupAddRequest.userIdList,userIdList]; arguments []; default message [userIdList]]; default message [Failed to convert property value of type 'java.lang.String[]' to required type 'com.fjlonge.management.model.request.ValidList' for property 'userIdList'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String[]' to required type 'com.fjlonge.management.model.request.ValidList' for property 'userIdList': no matching editors or conversion strategy found]


其实我很不明白一点,前端传入的字段,userIdList值为[1,2,8,9],系统可以对应到GroupAddRequest 下的userIdList,但是系统怎么知道把1,2,8,9对应到Admin的userId。所以感觉这个方法应该是行不通的。

好帮手慕小班 2020-11-04 19:31:30

同学你好,根据报错信息:Cannot convert value of type 'java.lang.String[]' to required type 'com.fjlonge.management.model.request.ValidList' for property 'userIdList'  -->无法转换类型的值java.lang.String[]'到所需类型'com.fjlonge.management.model.request.ValidList'中

猜测是传递数据的问题

    ​1、同学尝试单独传递list集合类型的数据,查看list类型的数据是否能正确传递

    ​2、如果list类型的数据可以正常传递,但是在类GroupAddRequest中ValidList不能传递的话,同学可以将自己完整的测试内容贴出,包括poatman中的测试内容也贴出,老师来复制测试一下。

好帮手慕小班 2020-11-04 18:11:18

同学你好,1、@Valid注解并不适用于List ,与List的书写位置并没有关系。

    2、同学想在GroupAddRequest 中使用List是有问题的,并不能直接这样用,建议参考如上案例:

    同学可以尝试定义一个自定义的ValidList:

public class ValidList<E> implements List<E> {
    @Valid
    private List<E> list;

    public ValidList() {
        this.list = new ArrayList<E>();
    }

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


userIdList中的Long类型封装到一个实体类Admin中

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;

public class Admin {
@NotNull(message = "用户id不能为null")
@Min(1)
Long userId;
}

在Admin中存在对应Long类型的userId,在controller中

@Data
public class GroupAddRequest {
@NotBlank(message = "name不能为空")
@Size(min = 2, max = 10, message = "name长度需要在2~10之间")
private String name;

@Min(value = 1, message = "adminId不能小于1")
private Long adminId;

private ValidList<Admin> userIdList;
}

通过这样的形式来尝试完成一下。



  • 提问者 慕丝1539783 #1
    我这个方法有试过,前端传同样的请求会报错 Field error in object 'groupAddRequest2' on field 'userIdList': rejected value [1,2,8]; codes [typeMismatch.groupAddRequest2.userIdList,typeMismatch.userIdList,typeMismatch.com.fjlonge.management.model.request.ValidList,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [groupAddRequest2.userIdList,userIdList]; arguments []; default message [userIdList]]; default message [Failed to convert property value of type 'java.lang.String[]' to required type 'com.fjlonge.management.model.request.ValidList' for property 'userIdList'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String[]' to required type 'com.fjlonge.management.model.request.ValidList' for property 'userIdList': no matching editors or conversion strategy found] 大概意思应该是前端传入的userIdList值为[1,2,8],不能套用ValidList<Admin>,似乎格式不对。
    2020-11-04 18:57:36
慕丝1539783 提问者 2020-11-04 16:06:34

老师,可能是我没表达清楚。

@RestController
@RequestMapping("/group")
public class GroupController {
    @Autowired
    private GroupService groupService;
    
    //新增小组
    @PostMapping("/add")
    public ApiRestResponse<Object> add(@Valid GroupAddRequest groupAddRequest) {
        groupService.add(groupAddRequest);
        return ApiRestResponse.success();
    }
}

这个是我的controller方法,传入的是GroupAddRequest这个请求模型,

@Data
public class GroupAddRequest {
    @NotBlank(message = "name不能为空")
    @Size(min = 2, max = 10, message = "name长度需要在2~10之间")
    private String name;

    @Min(value = 1, message = "adminId不能小于1")
    private Long adminId;

    private List<Long> userIdList;
}

前端传入post表单,

http://img1.sycdn.imooc.com//climg/5fa257b60843641715860499.jpg

这个要验证的userIdList不是直接出现在controller方法中,而是在GroupAddRequest这个请求模型中,而且List泛型是Long,如果用自定义类做泛型,那么前端传入的参数名就可能不是userIdList。

我希望是要验证的userIdList放在GroupAddRequest模型中,而controller方法传入只有GroupAddRequest这一个参数,然后在这个基础上,对GroupAddRequest中的userIdList进行校验

好帮手慕小班 2020-11-03 16:37:38

同学你好,1、如果只是添加@Valid注解并不适用于List ,原因是@Valid是JSR-303批注,JSR-303适用于JavaBean上的验证。而根据JavaBean的官方描述,java.util.List不是JavaBean,因此不能使用兼容JSR-303的验证器直接对其进行验证。

    2、如上所述,@Valid不能校验List ,此时其实可以自定义一个类,用来接收对应的数据,这样它就是一个JavaBean了。比如:

自定义的类CompanyTag

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;

public class CompanyTag {

    @NotNull(message = "用户id不能为null")
    @Min(1)
    Long userId;
    String name;
}

List改成自定义的ValidList<CompanyTag> 

public class ValidList<E> implements List<E> {

    @Valid
    private List<E> list;

    public ValidList() {
        this.list = new ArrayList<E>();
    }

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

在调用时

@PostMapping("add")
public Integer addBatch(@RequestBody @Valid ValidList<CompanyTag> companyTagList){  
  return 执行调用方法得到返回值;
}


3、当然同学也可以不用@Valid,直接在方法里手动书写校验内容。

  • 提问者 慕丝1539783 #1
    可能我没表达清楚,已经重新整理一下问题,麻烦解答一下。
    2020-11-04 16:07:55
好帮手慕小班 2020-11-02 19:03:08

同学可以参考楼上同学的方法来尝试一下

  • 提问者 慕丝1539783 #1
    不理解楼上的方法,貌似只是介绍如何开启验证,没写验证内容写哪里。 我的userIdList,只是普通的Long泛型,不是自定义类,不知道@Min和@Max注该标在哪来。 麻烦老师针对我这个问题,详细说明一下。
    2020-11-03 11:51:35
无敌小糖糖万岁 2020-11-02 18:51:19

其实@Valid只能校验JavaBean,而List不是JavaBean所以校验会失败,具体的方法你可以参考这个方法来实现对应List集合的验证

https://www.cnblogs.com/chen-chen-chen/p/11709536.html

问题已解决,确定采纳
还有疑问,暂不采纳

恭喜解决一个难题,获得1积分~

来为老师/同学的回答评分吧

0 星
请稍等 ...
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号

在线咨询

领取优惠

免费试听

领取大纲

扫描二维码,添加
你的专属老师