Spring Validation的校驗(yàn)順序問(wèn)題及解決過(guò)程
問(wèn)題場(chǎng)景
測(cè)試發(fā)現(xiàn)對(duì)同一個(gè)接口調(diào)用多次時(shí),返回的校驗(yàn)異常信息不同,經(jīng)過(guò)問(wèn)題追蹤,入?yún)?shí)體類(lèi)代碼如下:
@Data
public class EditDevNameDto {
@NotBlank(message = "deviceSn must not null")
private String deviceSn;
@NotBlank(message = "deviceName must not null")
private String deviceName;
}
當(dāng)這個(gè)接口入?yún)⒌膁eviceSn和deviceName均為空值時(shí),調(diào)用多次的話會(huì)出現(xiàn)兩個(gè)msg錯(cuò)誤信息循環(huán)返回的問(wèn)題。
原理剖析
懷疑是調(diào)用接口時(shí),校驗(yàn)注解的先后順序是不確定的,所以,可能deviceSn先被校驗(yàn),可能deviceName先被校驗(yàn),那要解決這個(gè)問(wèn)題就要規(guī)定校驗(yàn)的順序才行。
解決方法
使用@GroupSequence注解實(shí)現(xiàn)順序的穩(wěn)定性。
創(chuàng)建五個(gè)接口
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控制層代碼
注意在入?yún)⒅屑尤隌Validated(Group.class)注解,其中要加入被@GroupSequence修飾的類(lèi)對(duì)象。
@PostMapping("/edit_device_name")
public ExecuteResult editDevName(
@RequestBody @Validated(Group.class) EditDevNameDto dev) {
// -----邏輯代碼
return null;
}
修改實(shí)體類(lèi)代碼
在實(shí)體類(lèi)中使用校驗(yàn)注解中添加groups屬性,順序按照@GroupSequence類(lèi)規(guī)定的順序即可。
@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;
}
整改結(jié)果
當(dāng)這個(gè)接口入?yún)⒌膁eviceSn和deviceName均為空值時(shí),頻繁調(diào)用依然是按照先校驗(yàn)deviceSn,后校驗(yàn)deviceName的順序進(jìn)行參數(shù)校驗(yàn)。
后續(xù)問(wèn)題
問(wèn)題原因
解決了上面的問(wèn)題,過(guò)了幾天,又出現(xiàn)了解決校驗(yàn)順序的問(wèn)題,但是這次又不同于上一次,這次的Dto參數(shù)接收類(lèi)有些復(fù)雜,代碼如下:
@Data
public class UpdateInfoDto {
@NotBlank(message = "deviceSn can not be empty")
@GBDeviceSnValid
private String deviceSn;
@Valid // 讓CommonDto類(lèi)中的校驗(yàn)屬性生效
@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;
}
以上代碼中有兩個(gè)類(lèi),共同組合成參數(shù)接收類(lèi),要糾正校驗(yàn)順序混亂問(wèn)題,還是需要用到一開(kāi)始講到的@GroupSequence注解,但是,有兩個(gè)問(wèn)題:
- Dto中還有一個(gè)對(duì)象類(lèi)型的屬性。
- 對(duì)象類(lèi)型屬性中的子屬性(id、channelId)也要成功校驗(yàn)
解決方案
首先,為了讓Dto中的對(duì)象類(lèi)型的屬性也能正常校驗(yàn),需要添加@Valid注解;
然后,在對(duì)象類(lèi)型屬性中的子屬性中也需要像文章前面所說(shuō)的在校驗(yàn)注解中設(shè)置groups屬性。
修改后的代碼如下:
@Data
public class UpdateInfoDto {
@NotBlank(message = "deviceSn can not be empty", groups = {GroupA.class})
@GBDeviceSnValid(groups = {GroupA.class})
private String deviceSn;
@Valid // 讓CommonDto類(lèi)中的校驗(yàn)屬性生效
@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中校驗(yàn)屬性的注解不設(shè)置groups,CommonDto中的校驗(yàn)屬性就會(huì)失效,這是個(gè)大坑。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- javax.validation在Spring?Boot請(qǐng)求中使用方式
- spring中的參數(shù)校驗(yàn)技術(shù):jakarta.validation使用詳解
- SpringBoot @ConfigurationProperties + Validation實(shí)現(xiàn)啟動(dòng)期校驗(yàn)解決方案
- Spring Validation數(shù)據(jù)校驗(yàn)詳解
- SpringBoot利用validation實(shí)現(xiàn)數(shù)據(jù)校驗(yàn)完整指南
- 基于Spring Validation實(shí)現(xiàn)全局參數(shù)校驗(yàn)異常處理的示例詳解
- 如何在Spring Boot 項(xiàng)目中自定義 Validation 注解
相關(guān)文章
基于Properties實(shí)現(xiàn)配置數(shù)據(jù)庫(kù)驅(qū)動(dòng)
這篇文章主要介紹了基于Properties實(shí)現(xiàn)配置數(shù)據(jù)庫(kù)驅(qū)動(dòng),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05
springboot實(shí)現(xiàn)https雙向傳輸協(xié)議的示例代碼
本文主要介紹了springboot實(shí)現(xiàn)https雙向傳輸協(xié)議的示例代碼,包含配置證書(shū)和私鑰路徑、調(diào)用請(qǐng)求方法等步驟,具有一定的參考價(jià)值,感興趣的可以了解一下2025-03-03
Mybatis中特殊SQL的執(zhí)行的實(shí)現(xiàn)示例
本文主要介紹了Mybatis中特殊SQL的執(zhí)行的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
JAVA中Spring Security示例及常見(jiàn)問(wèn)題
文章概述Spring Security OAuth2與JWT模塊的版本兼容性及遷移建議,強(qiáng)調(diào)2.5.x支持JDK8但已棄用,推薦新項(xiàng)目使用SpringAuthorizationServer(Spring Boot3.x+),并指出依賴沖突、配置示例及密鑰安全注意事項(xiàng),感興趣的朋友一起看看吧2025-07-07
IDEA修改生成jar包名字的兩種方法實(shí)現(xiàn)
本文主要介紹了IDEA修改生成jar包名字的兩種方法實(shí)現(xiàn),通過(guò)簡(jiǎn)單的步驟,您可以修改項(xiàng)目名稱并在打包時(shí)使用新的名稱,具有一定的參考價(jià)值,感興趣的可以了解下2023-08-08
Spring-data-JPA使用時(shí)碰到的問(wèn)題以及解決方案
這篇文章主要介紹了Spring-data-JPA使用時(shí)碰到的問(wèn)題以及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12

