0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

SpringBoot Web應(yīng)用如何進(jìn)行參數(shù)校驗(yàn)?(下)

jf_78858299 ? 來源:JAVA旭陽 ? 作者:JAVA旭陽 ? 2023-05-11 10:37 ? 次閱讀

3. 分組校驗(yàn)

一個VO對象在新增的時候某些字段為必填,在更新的時候又非必填。如上面的ValidVO中 id`` 和 appId 屬性在新增操作時都是 非必填 ,而在編輯操作時都為 必填 ,name在新增操作時為 必填 ,面對這種場景你會怎么處理呢? 在實(shí)際開發(fā)中我見到很多同學(xué)都是建立兩個VO對象,ValidCreateVOValidEditVO來處理這種場景,這樣確實(shí)也能實(shí)現(xiàn)效果,但是會造成類膨脹。

其實(shí)Validator校驗(yàn)框架已經(jīng)考慮到了這種場景并且提供了解決方案,就是 分組校驗(yàn) ,只不過很多同學(xué)不知道而已。

要使用分組校驗(yàn),只需要三個步驟。

3.1. 第一步,定義分組接口

public interface ValidGroup extends Default {

    interface Crud extends ValidGroup{
        interface Create extends Crud{

        }

        interface Update extends Crud{

        }

        interface Query extends Crud{

        }

        interface Delete extends Crud{

        }
    }
}

這里我們定義一個分組接口ValidGroup讓其繼承javax.validation.groups.Default,再在分組接口中定義出多個不同的操作類型,Create,Update,Query,Delete。

3.2. 第二步,在模型中給參數(shù)分配分組

@Data
public class ValidVO {
    @Null(groups = ValidGroup.Crud.Create.class)
    @NotNull(groups = ValidGroup.Crud.Update.class, message = "應(yīng)用ID不能為空")
    private String id;

    @Length(min = 6,max = 12,message = "appId長度必須位于6到12之間")
    @Null(groups = ValidGroup.Crud.Create.class)
    @NotNull(groups = ValidGroup.Crud.Update.class, message = "應(yīng)用ID不能為空")
    private String appId;

    @NotBlank(message = "名字為必填項(xiàng)")
    @NotBlank(groups = ValidGroup.Crud.Create.class,message = "名字為必填項(xiàng)")
    private String name;

    @Email(message = "請?zhí)顚懻_的郵箱地址")
    private String email;

    @EnumString(value = {"F","M"}, message="性別只允許為F或M")
    private String sex;

    @NotEmpty(message = "級別不能為空")
    private String level;
}

給參數(shù)指定分組,對于未指定分組的則使用的是默認(rèn)分組。

3.3. 第三步,給需要參數(shù)校驗(yàn)的方法指定分組

@PostMapping(value = "/valid/add")
public String add(@Validated(value = ValidGroup.Crud.Create.class) ValidVO validVO){
 log.info("validEntity is {}", validVO);
 return "test3 valid success";
}

@PostMapping(value = "/valid/update")
public String update(@Validated(value = ValidGroup.Crud.Update.class) ValidVO validVO){
 log.info("validEntity is {}", validVO);
 return "test4 valid success";
}

這里我們通過value屬性給add()update()方法分別指定CreateUpdate分組

3.4. 測試

POST http://localhost:8080/valid/add
Content-Type: application/x-www-form-urlencoded

name=javadaily&level=12&email=476938977@qq.com&sex=F
  • Create操作

在Create時我們 沒有傳遞id和appId參數(shù) ,校驗(yàn)通過。

{
  "status": 100,
  "message": "操作成功",
  "data": "test3 valid success",
  "timestamp": 1652186105359
}
  • update操作

使用同樣的參數(shù)調(diào)用update方法時則提示參數(shù)校驗(yàn)錯誤

{
  "status": 400,
  "message": "ID不能為空; 應(yīng)用ID不能為空",
  "data": null,
  "timestamp": 1652186962377
}
復(fù)制代碼
  • 默認(rèn)校驗(yàn)生效操作

由于email屬于默認(rèn)分組,而我們的分組接口ValidGroup已經(jīng)繼承了Default分組,所以也是可以對email字段作參數(shù)校驗(yàn)的。故意寫錯email格式

POST http://localhost:8080/valid/add
Content-Type: application/x-www-form-urlencoded

/valid/update?name=javadaily&level=12&email=476938977&sex=F
{
  "status": 400,
  "message": "請?zhí)顚懻_的郵箱地址; ID不能為空; 應(yīng)用ID不能為空",
  "data": null,
  "timestamp": 1652187273865
}

4. 業(yè)務(wù)規(guī)則校驗(yàn)

業(yè)務(wù)規(guī)則校驗(yàn)指接口需要滿足某些特定的業(yè)務(wù)規(guī)則,舉個例子:業(yè)務(wù)系統(tǒng)的用戶需要保證其唯一性,用戶屬性不能與其他用戶產(chǎn)生沖突,不允許與數(shù)據(jù)庫中任何已有用戶的用戶名稱、手機(jī)號碼、郵箱產(chǎn)生重復(fù)。 這就要求在 創(chuàng)建用戶時需要校驗(yàn)用戶名稱、手機(jī)號碼、郵箱是否被注冊 ; 編輯用戶時不能將信息修改成已有用戶的屬性 。最優(yōu)雅的實(shí)現(xiàn)方法應(yīng)該是參考 **Bean Validation** 的標(biāo)準(zhǔn)方式,借助自定義校驗(yàn)注解完成業(yè)務(wù)規(guī)則校驗(yàn)。

4.1. 自定義注解

首先我們需要創(chuàng)建兩個自定義注解,用于業(yè)務(wù)規(guī)則校驗(yàn):

  • UniqueUser:表示一個用戶是唯一的,唯一性包含:用戶名,手機(jī)號碼、郵箱
@Documented
@Retention(RUNTIME)
@Target({FIELD, METHOD, PARAMETER, TYPE})
@Constraint(validatedBy = UserValidation.UniqueUserValidator.class)
public @interface UniqueUser {

    String message() default "用戶名、手機(jī)號碼、郵箱不允許與現(xiàn)存用戶重復(fù)";

    Class?[] groups() default {};

    Class? extends Payload[] payload() default {};
}
  • NotConflictUser:表示一個用戶的信息是無沖突的,無沖突是指該用戶的敏感信息與其他用戶不重合
@Documented
@Retention(RUNTIME)
@Target({FIELD, METHOD, PARAMETER, TYPE})
@Constraint(validatedBy = UserValidation.NotConflictUserValidator.class)
public @interface NotConflictUser {
    String message() default "用戶名稱、郵箱、手機(jī)號碼與現(xiàn)存用戶產(chǎn)生重復(fù)";

    Class?[] groups() default {};

    Class? extends Payload[] payload() default {};
}

4.2. 實(shí)現(xiàn)業(yè)務(wù)校驗(yàn)規(guī)則

想讓自定義驗(yàn)證注解生效,需要實(shí)現(xiàn) ConstraintValidator 接口。接口的第一個參數(shù)是 自定義注解類型 ,第二個參數(shù)是 被注解字段的類 ,因?yàn)樾枰r?yàn)多個參數(shù),我們直接傳入用戶對象。 需要提到的一點(diǎn)是 ConstraintValidator 接口的實(shí)現(xiàn)類無需添加 @Component 它在啟動的時候就已經(jīng)被加載到容器中了。

@Slf4j
public class UserValidation<T extends Annotation> implements ConstraintValidator<T, User> {

    protected Predicate

這里使用Predicate函數(shù)式接口對業(yè)務(wù)規(guī)則進(jìn)行判斷。

4.3. 測試代碼

@RestController
@RequestMapping("/senior/user")
@Slf4j
@Validated
public class UserController {
    @Autowired
    private UserRepository userRepository;


    @PostMapping
    public User createUser(@UniqueUser @Valid User user){
        User savedUser = userRepository.save(user);
        log.info("save user id is {}",savedUser.getId());
        return savedUser;
    }

    @SneakyThrows
    @PutMapping
    public User updateUser(@NotConflictUser @Valid @RequestBody User user){
        User editUser = userRepository.save(user);
        log.info("update user is {}",editUser);
        return editUser;
    }
}

使用很簡單,只需要在方法上加入自定義注解即可,業(yè)務(wù)邏輯中不需要添加任何業(yè)務(wù)規(guī)則的代碼。

POST http://localhost:8080/valid/add
Content-Type: application/json

    /senior/user

{
    "userName" : "100001"
}
{
	"status": 400,
	"message": "用戶名、手機(jī)號碼、郵箱不允許與現(xiàn)存用戶重復(fù)",
	"data": null,
	"timestamp": 1652196524725
}

5. 總結(jié)

通過上面幾步操作,業(yè)務(wù)校驗(yàn)便和業(yè)務(wù)邏輯就完全分離開來,在需要校驗(yàn)時用@Validated注解自動觸發(fā),或者通過代碼手動觸發(fā)執(zhí)行,可根據(jù)你們項(xiàng)目的要求,將這些注解應(yīng)用于控制器、服務(wù)層、持久層等任何層次的代碼之中。 這種方式比任何業(yè)務(wù)規(guī)則校驗(yàn)的方法都優(yōu)雅,推薦大家在項(xiàng)目中使用。在開發(fā)時可以將不帶業(yè)務(wù)含義的格式校驗(yàn)注解放到 Bean 的類定義之上,將帶業(yè)務(wù)邏輯的校驗(yàn)放到 Bean 的類定義的外面。這兩者的區(qū)別是放在類定義中的注解能夠自動運(yùn)行,而放到類外面則需要像前面代碼那樣,明確標(biāo)出注解時才會運(yùn)行。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 接口
    +關(guān)注

    關(guān)注

    33

    文章

    8355

    瀏覽量

    150517
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4694

    瀏覽量

    68078
  • Validator驗(yàn)
    +關(guān)注

    關(guān)注

    0

    文章

    3

    瀏覽量

    5775
  • SpringBoot
    +關(guān)注

    關(guān)注

    0

    文章

    173

    瀏覽量

    153
收藏 人收藏

    評論

    相關(guān)推薦

    SpringBoot 如何實(shí)現(xiàn)熱部署

    熱部署是軟件開發(fā)中一個非常有用的功能,它允許我們在不重新啟動整個應(yīng)用的情況,依舊能夠使我們修改的代碼生效。 現(xiàn)在Java Web 開發(fā)應(yīng)該都是使用的 SpringBoot,那么本篇文章就來介紹
    的頭像 發(fā)表于 09-30 10:16 ?779次閱讀
    <b class='flag-5'>SpringBoot</b> 如何實(shí)現(xiàn)熱部署

    SpringBoot知識總結(jié)

    SpringBoot干貨學(xué)習(xí)總結(jié)
    發(fā)表于 08-01 10:40

    請問windowshost文件如何進(jìn)行修改與刷新?

    windowshost文件如何進(jìn)行修改與刷新
    發(fā)表于 11-10 07:00

    變頻器的參數(shù)何進(jìn)行設(shè)置呢

    變頻器的參數(shù)有數(shù)百甚至上千個,對這些參數(shù)進(jìn)行合理正確的設(shè)置是使變頻器高效運(yùn)行并且滿足用戶要求的前提,那么,如何進(jìn)行設(shè)置呢?本文以西門子MicroMaster440變頻器為例
    發(fā)表于 09-03 07:43

    怎樣去使用springboot

    怎樣去使用springboot呢?學(xué)習(xí)springboot需要懂得哪些?
    發(fā)表于 10-25 07:13

    SpringBoot應(yīng)用啟動運(yùn)行run方法

    )、refreshContext(context);SpringBoot刷新IOC容器【創(chuàng)建IOC容器對象,并初始化容器,創(chuàng)建容器中的每一個組件】;如果是web應(yīng)用創(chuàng)建**AnnotationConfigEmbeddedWebApplicationContext**,否則
    發(fā)表于 12-20 06:16

    Springboot是如何獲取自定義異常并進(jìn)行返回的

    這里看到新服務(wù)是封裝的自定義異常,準(zhǔn)備入手剖析一,自定義的異常是如何進(jìn)行抓住我們請求的方法的異常,并進(jìn)行封裝返回到。廢話不多說,先看看如何才能實(shí)現(xiàn)封裝異常,先來一個示例:在這里,您會看到新服務(wù)是一
    發(fā)表于 03-22 14:15

    何進(jìn)行OPCDCOM配置

    何進(jìn)行OPCDCOM配置(四會理士電源技術(shù)有限公司招聘)-如何進(jìn)行OPCDCOM配置? ? ? ? ? ? ? ? ? ? ??
    發(fā)表于 09-18 14:23 ?11次下載
    如<b class='flag-5'>何進(jìn)行</b>OPCDCOM配置

    增強(qiáng)FIFO模式的奇偶校驗(yàn)

    自昊芯推出專題講解SCI串口通訊奇偶校驗(yàn),分為兩期講解,上期主要講解標(biāo)準(zhǔn)SCI模式的奇偶校驗(yàn),本期主要講解增強(qiáng)FIFO模式的奇偶校驗(yàn)。
    的頭像 發(fā)表于 11-02 09:30 ?935次閱讀

    如何用責(zé)任鏈默認(rèn)優(yōu)雅地進(jìn)行參數(shù)校驗(yàn)

    那么有什么更好的參數(shù)校驗(yàn)的方式呢?本文就推薦一種通過責(zé)任鏈設(shè)計(jì)模式來優(yōu)雅地實(shí)現(xiàn)參數(shù)校驗(yàn)功能,我們通過一個用戶注冊的例子來講明白如何實(shí)現(xiàn)。
    的頭像 發(fā)表于 04-06 15:00 ?419次閱讀

    什么是 SpringBoot

    本文從為什么要有 `SpringBoot`,以及 `SpringBoot` 到底方便在哪里開始入手,逐步分析了 `SpringBoot` 自動裝配的原理,最后手寫了一個簡單的 `start` 組件,通過實(shí)戰(zhàn)來體會了 `
    的頭像 發(fā)表于 04-07 11:28 ?1223次閱讀
    什么是 <b class='flag-5'>SpringBoot</b>?

    SpringBoot的核心注解1

    今天跟大家來探討SpringBoot的核心注解@SpringBootApplication以及run方法,理解下springBoot為什么不需要XML,達(dá)到零配置
    的頭像 發(fā)表于 04-07 14:34 ?640次閱讀
    <b class='flag-5'>SpringBoot</b>的核心注解1

    SpringBoot的核心注解2

    今天跟大家來探討SpringBoot的核心注解@SpringBootApplication以及run方法,理解下springBoot為什么不需要XML,達(dá)到零配置
    的頭像 發(fā)表于 04-07 14:34 ?1897次閱讀
    <b class='flag-5'>SpringBoot</b>的核心注解2

    SpringBoot Web應(yīng)用如何進(jìn)行參數(shù)校驗(yàn)?(上)

    的話就太繁瑣了,代碼可讀性極差。**Validator框架**就是為了解決開發(fā)人員在開發(fā)的時候少寫代碼,提升開發(fā)效率;Validator專門用來進(jìn)行接口參數(shù)校驗(yàn),例如常見的必填校驗(yàn),e
    的頭像 發(fā)表于 05-11 10:31 ?553次閱讀

    javaweb和springboot能一起用嗎

    框架來開發(fā) Web 應(yīng)用程序。 首先,讓我們了解一 JavaWeb 和 SpringBoot 的基本概念。 JavaWeb 是一種用于開發(fā)基于 Java 技術(shù)的 Web 應(yīng)用程序的技
    的頭像 發(fā)表于 11-16 10:54 ?1849次閱讀