java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Spring Validation的校验顺序问题

Spring Validation的校验顺序问题及解决过程

作者:她又在丛中笑

本文主要介绍了使用@GroupSequence注解来解决接口入参校验顺序不稳定的问题,并提供了具体的实现步骤和代码示例

问题场景

测试发现对同一个接口调用多次时,返回的校验异常信息不同,经过问题追踪,入参实体类代码如下:

@Data
public class EditDevNameDto {
    @NotBlank(message = "deviceSn must not null")
    private String deviceSn;
    @NotBlank(message = "deviceName must not null")
    private String deviceName;
}

当这个接口入参的deviceSn和deviceName均为空值时,调用多次的话会出现两个msg错误信息循环返回的问题。

原理剖析

怀疑是调用接口时,校验注解的先后顺序是不确定的,所以,可能deviceSn先被校验,可能deviceName先被校验,那要解决这个问题就要规定校验的顺序才行。

解决方法

使用@GroupSequence注解实现顺序的稳定性。

创建五个接口

public interface GroupA {
}

public interface GroupB {
}

public interface GroupC {
}

public interface GroupD {
}

@GroupSequence({GroupA.class,GroupB.class,GroupC.class,GroupD.class})
public interface Group {
}

修改Controller控制层代码

注意在入参中加入@Validated(Group.class)注解,其中要加入被@GroupSequence修饰的类对象。

    @PostMapping("/edit_device_name")
    public ExecuteResult editDevName(
    	@RequestBody @Validated(Group.class) EditDevNameDto dev) {
        // -----逻辑代码
        return null;
    }

修改实体类代码

在实体类中使用校验注解中添加groups属性,顺序按照@GroupSequence类规定的顺序即可。

@Data
public class EditDevNameDto {
    @NotBlank(message = "deviceSn must not null", groups = {GroupA.class})
    private String deviceSn;
    @NotBlank(message = "deviceName must not null", groups = {GroupB.class})
    private String deviceName;
}

整改结果

当这个接口入参的deviceSn和deviceName均为空值时,频繁调用依然是按照先校验deviceSn,后校验deviceName的顺序进行参数校验。

后续问题

问题原因

解决了上面的问题,过了几天,又出现了解决校验顺序的问题,但是这次又不同于上一次,这次的Dto参数接收类有些复杂,代码如下:

@Data
public class UpdateInfoDto {

    @NotBlank(message = "deviceSn can not be empty")
    @GBDeviceSnValid
    private String deviceSn;


    @Valid // 让CommonDto类中的校验属性生效
    @GBChannelDuplicateValid
    @CollectionNotEmptyValid(message = "channels cannot be empty")
    private List<CommonDto> channels;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonDto{

    @GBChannelIdValid
    private String id;

    @NotNull(message = "channelId must not be null")
    @PositiveOrZero(message = "channelId only Integer or zero")
    private Integer channelId;
}

以上代码中有两个类,共同组合成参数接收类,要纠正校验顺序混乱问题,还是需要用到一开始讲到的@GroupSequence注解,但是,有两个问题:

  1. Dto中还有一个对象类型的属性。
  2. 对象类型属性中的子属性(id、channelId)也要成功校验

解决方案

首先,为了让Dto中的对象类型的属性也能正常校验,需要添加@Valid注解;

然后,在对象类型属性中的子属性中也需要像文章前面所说的在校验注解中设置groups属性。

修改后的代码如下:

@Data
public class UpdateInfoDto {

    @NotBlank(message = "deviceSn can not be empty", groups = {GroupA.class})
    @GBDeviceSnValid(groups = {GroupA.class})
    private String deviceSn;


    @Valid // 让CommonDto类中的校验属性生效
    @GBChannelDuplicateValid(groups = {GroupB.class})
    @CollectionNotEmptyValid(message = "channels cannot be empty", groups = {GroupB.class})
    private List<CommonDto> channels;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonDto{

    @GBChannelIdValid(groups = {GroupC.class})
    private String id;

    @NotNull(message = "channelId must not be null", groups = {GroupD.class})
    @PositiveOrZero(message = "channelId only Integer or zero", groups = {GroupD.class})
    private Integer channelId;
}

如果CommonDto中校验属性的注解不设置groups,CommonDto中的校验属性就会失效,这是个大坑

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文