Bean Validation 校驗(yàn)實(shí)踐

前言

在應(yīng)用程序的業(yè)務(wù)邏輯中,經(jīng)常會(huì)碰到需要校驗(yàn)參數(shù)的情況。
經(jīng)常要判斷一個(gè) Model 的字段是否為 Null 之類,或者判斷長(zhǎng)度等。
那么在代碼層面上,就會(huì)需要編寫很多校驗(yàn),影響代碼的閱讀以及維護(hù),而且造成代碼的冗余。

應(yīng)用程序必須通過(guò)某種手段來(lái)確保輸入進(jìn)來(lái)的數(shù)據(jù)從語(yǔ)義上來(lái)講是正確的。

在通常的情況下,應(yīng)用程序是分層的,不同的層由不同的開發(fā)人員來(lái)完成。很多時(shí)候同樣的數(shù)據(jù)驗(yàn)證邏輯會(huì)出現(xiàn)在不同的層,這樣就會(huì)導(dǎo)致代碼冗余和一些管理的問(wèn)題,比如說(shuō)語(yǔ)義的一致性等。為了避免這樣的情況發(fā)生,最好是將驗(yàn)證邏輯與相應(yīng)的域模型進(jìn)行綁定。

為了更好的關(guān)注核心的業(yè)務(wù)邏輯,減少參數(shù)校驗(yàn)侵入方法內(nèi)部,因此有了 JRS-303 提案。

JSR是Java Specification Requests的縮寫,意思是Java 規(guī)范提案。是指向JCP(Java Community Process)提出新增一個(gè)標(biāo)準(zhǔn)化技術(shù)規(guī)范的正式請(qǐng)求。任何人都可以提交JSR,以向Java平臺(tái)增添新的API和服務(wù)。JSR已成為Java界的一個(gè)重要標(biāo)準(zhǔn)。

Bean Validation 為 JavaBean 驗(yàn)證定義了相應(yīng)的元數(shù)據(jù)模型和 API。缺省的元數(shù)據(jù)是 Java Annotations,通過(guò)使用 XML 可以對(duì)原有的元數(shù)據(jù)信息進(jìn)行覆蓋和擴(kuò)展。在應(yīng)用程序中,通過(guò)使用 Bean Validation 或是你自己定義的 constraint,例如 @NotNull, @Max, @ZipCode, 就可以確保數(shù)據(jù)模型(JavaBean)的正確性。constraint 可以附加到字段,getter 方法,類或者接口上面。對(duì)于一些特定的需求,用戶可以很容易的開發(fā)定制化的 constraint。Bean Validation 是一個(gè)運(yùn)行時(shí)的數(shù)據(jù)驗(yàn)證框架,在驗(yàn)證之后驗(yàn)證的錯(cuò)誤信息會(huì)被馬上返回。

Hibernate Validator 是 Bean Validation 的參考實(shí)現(xiàn) . Hibernate Validator 提供了 JSR 303 規(guī)范中所有內(nèi)置 constraint 的實(shí)現(xiàn),除此之外還有一些附加的 constraint。如果想了解更多有關(guān) Hibernate Validator 的信息,請(qǐng)查看 http://www.hibernate.org/subprojects/validator.html

下載 JSR 303 – Bean Validation 規(guī)范 http://jcp.org/en/jsr/detail?id=303
目前最新的為 Bean Validation 2.0,是 JSR 380,實(shí)現(xiàn)為 Hibernate Validator - 6.0.1.Final

Bean Validation 2.0 中的 constraint

Constraint 詳細(xì)信息
@Null 被注釋的元素必須為 null
@NotNull 被注釋的元素必須不為 null
@AssertTrue 被注釋的元素必須為 true
@AssertFalse 被注釋的元素必須為 false
@Min(value) 被注釋的元素必須是一個(gè)數(shù)字,其值必須大于等于指定的最小值
@Max(value) 被注釋的元素必須是一個(gè)數(shù)字,其值必須小于等于指定的最大值
@DecimalMin(value) 被注釋的元素必須是一個(gè)數(shù)字,其值必須大于等于指定的最小值
@DecimalMax(value) 被注釋的元素必須是一個(gè)數(shù)字,其值必須小于等于指定的最大值
@Negative 被注釋的元素必須是一個(gè)嚴(yán)格的負(fù)數(shù),舉例,0 被視為無(wú)效值,因?yàn)椴皇秦?fù)數(shù)
@NegativeOrZero 被注釋的元素必須是一個(gè)嚴(yán)格的負(fù)數(shù)或者 0
@Positive 被注釋的元素必須是一個(gè)嚴(yán)格的正數(shù),舉例,0 被視為無(wú)效值,因?yàn)椴皇钦龜?shù)
@PositiveOrZero 被注釋的元素必須是一個(gè)嚴(yán)格的正數(shù)或者 0
@Size(max, min) 被注釋的元素的大小必須在指定的范圍內(nèi)
@Digits (integer, fraction) 被注釋的元素必須是一個(gè)帶小數(shù)的數(shù),integer 為整數(shù)位最大值,fraction 為小數(shù)位最大值
@Past 被注釋的元素必須是一個(gè)過(guò)去的日期
@PastOrPresent 被注釋的元素必須是一個(gè)過(guò)去的日期或者現(xiàn)在日期
@Future 被注釋的元素必須是一個(gè)將來(lái)的日期
@FutureOrPresent 被注釋的元素必須是一個(gè)將來(lái)的日期或者現(xiàn)在日期
@Pattern(value) 被注釋的元素必須符合指定的正則表達(dá)式
@NotEmpty 被注釋的元素必須不為 null 或者不為空,可校驗(yàn)字符、集合、Map
@NotBlank 被注釋的元素必須不為空字符串
@Email 被注釋的元素必須是電子郵箱地址

Hibernate Validator 附加的 constraint

Constraint 詳細(xì)信息
@Length 被注釋的字符串的大小必須在指定的范圍內(nèi)
@Range 被注釋的元素必須在合適的范圍內(nèi)

Spring boot 中使用方式

參數(shù)校驗(yàn)

@Slf4j
@Validated // 使用 spring 的 Validated 注解標(biāo)注此 Controller 是需要執(zhí)行校驗(yàn)的,
@RestController
@RequestMapping("/demo-1")
public class Demo1Controller {

    /**
     * 在參數(shù)上做校驗(yàn),基本類型
     * @param name 名稱
     *
     * @return
     */
    @GetMapping("/query-1")
    public HttpStatus query1(@NotBlank(message = "不能為空") String name) {
        log.info("name is {}", name);
        return HttpStatus.OK;
    }

}

運(yùn)行結(jié)果:


POJO 校驗(yàn)

@Slf4j
@RestController
@RequestMapping("/demo-2")
public class Demo2Controller {

    @PostMapping("/save-1")
    public HttpStatus save1(@Validated Person person) {
        return HttpStatus.OK;
    }

}

@Data
public class Person {

    @NotBlank(message = "名字不能為空")
    private String name;

    private Integer age;

    private String sex;
}

運(yùn)行結(jié)果:


接口層級(jí)校驗(yàn)

@Slf4j
@RestController
@RequestMapping("/demo-3")
public class Demo3Controller {

    @Resource
    private DemoService service;

    @PostMapping("/save-1")
    public HttpStatus save1(@RequestBody Person person) {
        log.info("person is {}", person);
        service.save(person);

        return HttpStatus.OK;
    }

}

@Data
public class Person {

    @NotBlank(message = "名字不能為空")
    private String name;

    private Integer age;

    private String sex;
}

@Validated
public interface DemoService {

    void save(@Valid Person person);
}

運(yùn)行結(jié)果:


分組校驗(yàn)

@Slf4j
@RestController
@RequestMapping("/demo-4")
public class Demo4Controller {

    @PostMapping("/save-1")
    public HttpStatus save1(@Validated(value = {SaveValidation.class, Default.class}) @RequestBody PersonGroup person) {
        log.info("person is {}", person);

        return HttpStatus.OK;
    }

    @PostMapping("/update-1")
    public HttpStatus update1(@Validated(value = {UpdateValidation.class, Default.class}) @RequestBody PersonGroup person) {
        log.info("person is {}", person);

        return HttpStatus.OK;
    }

}

@Data
public class PersonGroup {

    @NotBlank(groups = {SaveValidation.class}, message = "保存時(shí)名字不能為空")
    private String saveName;

    @NotBlank(groups = {UpdateValidation.class}, message = "更新時(shí)名字不能為空")
    private String updateName;

    private Integer age;

    private String sex;
}

運(yùn)行結(jié)果:


@Validated 隱含默認(rèn)校驗(yàn),即默認(rèn)分組,如果不分場(chǎng)景下校驗(yàn),可以使用如下方式配置:
分組時(shí):
@Validated(value = {ContainerSaveValidation.class, Default.class})
屬性上:
@NotBlank(groups = {SaveValidation.class}, message = "保存時(shí)名字不能為空") private String saveName;

參考文章:
https://www.ibm.com/developerworks/cn/java/j-lo-jsr303/index.html
Hibernate Validator 官方文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容