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

可以說(shuō)幾乎所有的應(yīng)用場(chǎng)景中,參數(shù)驗(yàn)證都在編寫(xiě)業(yè)務(wù)邏輯前完成,嚴(yán)格確保進(jìn)來(lái)的數(shù)據(jù)是合法且符合要求的。

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

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

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

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

@Data
public class Validate {

    // 空和非空檢查: @Null、@NotNull、@NotBlank、@NotEmpty

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

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

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

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

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

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

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

    // 長(zhǎng)度檢查: @Size、@Length

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

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

    // 日期檢查: @Future、@FutureOrPresent、@Past、@PastOrPresent

    @Future(message = "驗(yàn)證日期是否在當(dāng)前時(shí)間之后")
    private Date futureDate;

    @FutureOrPresent(message = "驗(yàn)證日期是否為當(dāng)前時(shí)間或之后")
    private Date futureOrPresentDate;

    @Past(message = "驗(yàn)證日期是否在當(dāng)前時(shí)間之前")
    private Date pastDate;

    @PastOrPresent(message = "驗(yàn)證日期是否為當(dāng)前時(shí)間或之前")
    private Date pastOrPresentDate;

    // 其它檢查: @Email、@CreditCardNumber、@URL、@Pattern、@ScriptAssert、@UniqueElements

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

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

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

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

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

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

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

        private Date startDate;

        private Date endDate;

    }

    // 數(shù)值檢查: @Min、@Max、@Range、@DecimalMin、@DecimalMax、@Digits

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

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

}

常見(jiàn)的前后端分離開(kāi)發(fā)模式,數(shù)據(jù)通信通常以 JSON 為主。針對(duì) POST 和 PUT 請(qǐng)求,一般通過(guò)新建域(對(duì)象)模型來(lái)進(jìn)行數(shù)據(jù)綁定和校驗(yàn),constraint 通常附加在這些域模型的字段上(如上):

     /**
     * Valid注解標(biāo)明要對(duì)參數(shù)對(duì)象進(jìn)行數(shù)據(jù)校驗(yàn)
     */
    @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;
    }

此外,對(duì)于 GET 和 DELETE 請(qǐng)求,參數(shù)通常為 key1=value1&key2=value2 這種形式。默認(rèn)情況下,Hibernate Validator 只能對(duì) Object 屬性進(jìn)行校驗(yàn),并不能對(duì)單個(gè)參數(shù)進(jìn)行校驗(yàn),Spring 在此基礎(chǔ)上進(jìn)行了擴(kuò)展,通過(guò)配置 MethodValidationPostProcessor 處理器,可以實(shí)現(xiàn)對(duì)方法參數(shù)的攔截校驗(yàn)。

@Configuration
public class ValidateConfig {

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

}

注意,要在 Controller 類上明確標(biāo)明 @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 = "郵箱格式錯(cuò)誤") 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ù)要是校驗(yàn)失敗,錯(cuò)誤提示明顯并不友好,通過(guò)捕獲此類異常就可以解決,這里就不再介紹了。

參考鏈接

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

示例源碼

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

?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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