Java 服務端參數(shù)校驗 - JSR 303 介紹及實踐

可以說幾乎所有的應用場景中填帽,參數(shù)驗證都在編寫業(yè)務邏輯前完成,嚴格確保進來的數(shù)據(jù)是合法且符合要求的。

Java Web 開發(fā)領(lǐng)域沥邻,也早有較為完善的 Bean Validation 為 Java Bean 驗證定義了相應的元數(shù)據(jù)模型和 API平项。首先赫舒,在項目中引入 web 模塊的依賴:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

Hibernate Validator 是 Bean Validation 的一種實現(xiàn) . Hibernate Validator 提供了 JSR 303 規(guī)范中所有內(nèi)置 constraint 的實現(xiàn),以及一些附加的 constraint闽瓢。如果想了解更多請查看 http://www.hibernate.org/subprojects/validator.html

具體以及常用的 constraint 包含如下:

@Data
public class Validate {

    // 空和非空檢查: @Null接癌、@NotNull、@NotBlank扣讼、@NotEmpty

    @Null(message = "驗證是否為 null")
    private Integer isNull;

    @NotNull(message = "驗證是否不為 null, 但無法查檢長度為0的空字符串")
    private Integer id;

    @NotBlank(message = "檢查字符串是不是為 null缺猛,以及去除空格后長度是否大于0")
    private String name;

    @NotEmpty(message = "檢查是否為 NULL 或者是 EMPTY")
    private List<String> stringList;

    // Boolean值檢查: @AssertTrue、@AssertFalse

    @AssertTrue(message = " 驗證 Boolean參數(shù)是否為 true")
    private Boolean isTrue;

    @AssertFalse(message = "驗證 Boolean 參數(shù)是否為 false ")
    private Boolean isFalse;

    // 長度檢查: @Size椭符、@Length

    @Size(min = 1, max = 2, message = "驗證(Array,Collection,Map,String)長度是否在給定范圍內(nèi)")
    private List<Integer> integerList;

    @Length(min = 8, max = 30, message = "驗證字符串長度是否在給定范圍內(nèi)")
    private String address;

    // 日期檢查: @Future枯夜、@FutureOrPresent、@Past艰山、@PastOrPresent

    @Future(message = "驗證日期是否在當前時間之后")
    private Date futureDate;

    @FutureOrPresent(message = "驗證日期是否為當前時間或之后")
    private Date futureOrPresentDate;

    @Past(message = "驗證日期是否在當前時間之前")
    private Date pastDate;

    @PastOrPresent(message = "驗證日期是否為當前時間或之前")
    private Date pastOrPresentDate;

    // 其它檢查: @Email湖雹、@CreditCardNumber、@URL曙搬、@Pattern摔吏、@ScriptAssert、@UniqueElements

    @Email(message = "校驗是否為正確的郵箱格式")
    private String email;

    @CreditCardNumber(message = "校驗是否為正確的信用卡號")
    private String creditCardNumber;

    @URL(protocol = "http", host = "127.0.0.1", port = 8080, message = "校驗是否為正確的URL地址")
    private String url;

    @Pattern(regexp = "^1[3|4|5|7|8][0-9]{9}$", message = "正則校驗是否為正確的手機號")
    private String phone;

    // 對關(guān)聯(lián)對象元素進行遞歸校驗檢查

    @Valid
    @UniqueElements(message = "校驗集合中的元素是否唯一")
    private List<CalendarEvent> calendarEvent;

    @Data
    @ScriptAssert(lang = "javascript", script = "_this.startDate.before(_this.endDate)",
            message = "通過腳本表達式校驗參數(shù)")
    private class CalendarEvent {

        private Date startDate;

        private Date endDate;

    }

    // 數(shù)值檢查: @Min纵装、@Max征讲、@Range、@DecimalMin橡娄、@DecimalMax诗箍、@Digits

    @Min(value = 0, message = "驗證數(shù)值是否大于等于指定值")
    @Max(value = 100, message = "驗證數(shù)值是否小于等于指定值")
    @Range(min = 0, max = 100, message = "驗證數(shù)值是否在指定值區(qū)間范圍內(nèi)")
    private Integer score;

    @DecimalMin(value = "10.01", inclusive = false, message = "驗證數(shù)值是否大于等于指定值")
    @DecimalMax(value = "199.99", message = "驗證數(shù)值是否小于等于指定值")
    @Digits(integer = 3, fraction = 2, message = "限制整數(shù)位最多為3,小數(shù)位最多為2")
    private BigDecimal money;

}

常見的前后端分離開發(fā)模式挽唉,數(shù)據(jù)通信通常以 JSON 為主滤祖。針對 POST 和 PUT 請求,一般通過新建域(對象)模型來進行數(shù)據(jù)綁定和校驗瓶籽,constraint 通常附加在這些域模型的字段上(如上):

     /**
     * Valid注解標明要對參數(shù)對象進行數(shù)據(jù)校驗
     */
    @PutMapping
    @PostMapping
    public Map<String, Object> test01(@RequestBody @Valid Validate validate, BindingResult bindingResult) {
        Map<String, Object> map = new HashMap<>(4);

        if (bindingResult.hasErrors()) {
            String errorMsg = bindingResult.getFieldErrors().stream().map(FieldError::getDefaultMessage)
                    .collect(Collectors.joining(","));
            map.put("errorMsg", errorMsg);
        }

        map.put("params", validate.toString());
        return map;
    }

此外匠童,對于 GET 和 DELETE 請求,參數(shù)通常為 key1=value1&key2=value2 這種形式塑顺。默認情況下汤求,Hibernate Validator 只能對 Object 屬性進行校驗俏险,并不能對單個參數(shù)進行校驗,Spring 在此基礎上進行了擴展扬绪,通過配置 MethodValidationPostProcessor 處理器竖独,可以實現(xiàn)對方法參數(shù)的攔截校驗。

@Configuration
public class ValidateConfig {

    /**
     * 配置MethodValidationPostProcessor攔截器挤牛,以實現(xiàn)對方法參數(shù)的校驗
     */
    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        return new MethodValidationPostProcessor();
    }

}

注意莹痢,要在 Controller 類上明確標明 @Validated

@Validated
@RestController
@RequestMapping("validate")
public class ValidateController {

    @GetMapping
    @DeleteMapping
    public Map<String, Object> test02(@NotNull(message = "id不能為空") @Range(min = 1, max = 100, message = "id最小為1最大為100") Integer id,
                                                           @NotBlank(message = "email不能為空") @Email(message = "郵箱格式錯誤") String email,
                                                           @ModelAttribute @Valid Validate validate) {
        Map<String, Object> map = new HashMap<>(4);

        map.put("id", id);
        map.put("email", email);
        map.put("params", validate.toString());

        return map;
    }

}

上述這種形式的參數(shù)要是校驗失敗,錯誤提示明顯并不友好赊颠,通過捕獲此類異常就可以解決格二,這里就不再介紹了劈彪。

參考鏈接

JSR 303 - Bean Validation 介紹及最佳實踐
SpringBoot-服務端參數(shù)驗證-JSR-303驗證框架

示例源碼

文章已授權(quán)轉(zhuǎn)載竣蹦,原文鏈接:Spring Boot 項目參數(shù)校驗的常見使用場景

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市沧奴,隨后出現(xiàn)的幾起案子痘括,更是在濱河造成了極大的恐慌,老刑警劉巖滔吠,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纲菌,死亡現(xiàn)場離奇詭異,居然都是意外死亡疮绷,警方通過查閱死者的電腦和手機翰舌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來冬骚,“玉大人椅贱,你說我怎么就攤上這事≈欢常” “怎么了庇麦?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長喜德。 經(jīng)常有香客問我山橄,道長,這世上最難降的妖魔是什么舍悯? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任航棱,我火速辦了婚禮,結(jié)果婚禮上萌衬,老公的妹妹穿的比我還像新娘丧诺。我一直安慰自己,他們只是感情好奄薇,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布驳阎。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪呵晚。 梳的紋絲不亂的頭發(fā)上蜘腌,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音饵隙,去河邊找鬼撮珠。 笑死,一個胖子當著我的面吹牛金矛,可吹牛的內(nèi)容都是我干的芯急。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼驶俊,長吁一口氣:“原來是場噩夢啊……” “哼娶耍!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起饼酿,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤榕酒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后故俐,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體想鹰,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年药版,在試婚紗的時候發(fā)現(xiàn)自己被綠了辑舷。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡槽片,死狀恐怖何缓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情筐乳,我是刑警寧澤歌殃,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站蝙云,受9級特大地震影響氓皱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜勃刨,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一波材、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧身隐,春花似錦廷区、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽埠帕。三九已至,卻和暖如春玖绿,著一層夾襖步出監(jiān)牢的瞬間稽揭,已是汗流浹背苍狰。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留须床,地道東北人随闪。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓乍丈,卻偏偏與公主長得像桐款,于是被迫代替她去往敵國和親绢掰。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容