溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

SpringBoot分組校驗(yàn)及自定義校驗(yàn)注解是怎樣的

發(fā)布時間:2021-09-29 17:07:21 來源:億速云 閱讀:169 作者:柒染 欄目:編程語言

這篇文章給大家介紹SpringBoot分組校驗(yàn)及自定義校驗(yàn)注解是怎樣的,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

前言

??在日常的開發(fā)中,參數(shù)校驗(yàn)是非常重要的一個環(huán)節(jié),嚴(yán)格參數(shù)校驗(yàn)會減少很多出bug的概率,增加接口的安全性。這篇則是介紹一些進(jìn)階的校驗(yàn)方式。比如說:在某個接口編寫的過程中肯定會遇到,當(dāng)xxType值為A,paramA值必傳。xxType值為B,paramB值必須傳。對于這樣的,通常的做法就是在controller加上各種if判斷。顯然這樣的代碼是不夠優(yōu)雅的,而分組校驗(yàn)及自定義參數(shù)校驗(yàn),就是來解決這個問題的。

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

??Restful的接口,在現(xiàn)在來講應(yīng)該是比較常見的了,常用的地址欄的參數(shù),我們都是這樣校驗(yàn)的。

/**
 * 獲取電話號碼信息
 */
@GetMapping("/phoneInfo/{phone}")
public ResultVo phoneInfo(@PathVariable("phone") String phone){
    // 驗(yàn)證電話號碼是否有效
    String pattern = "^[1][3,4,5,7,8][0-9]{9}$";
    boolean isValid =  Pattern.matches(pattern, phone);
    if(isValid){
        // 執(zhí)行相應(yīng)邏輯
        return ResultVoUtil.success(phone);
    } else {
        // 返回錯誤信息
        return ResultVoUtil.error("手機(jī)號碼無效");
    }
}

很顯然上面的代碼不夠優(yōu)雅,所以我們可以在參數(shù)后面,添加對應(yīng)的正則表達(dá)式phone:正則表達(dá)式來進(jìn)行驗(yàn)證。這樣就省去了在controller編寫校驗(yàn)代碼了。

/**
 * 獲取電話號碼信息
 */
@GetMapping("/phoneInfo/{phone:^[1][3,4,5,7,8][0-9]{9}$}")
public ResultVo phoneInfo(@PathVariable("phone") String phone){
    return ResultVoUtil.success(phone);
}

雖然這樣處理后代碼更精簡了。但是如果傳入的手機(jī)號碼,不符合規(guī)則會直接返回404。而不是提示手機(jī)號碼錯誤。錯誤信息如下:

SpringBoot分組校驗(yàn)及自定義校驗(yàn)注解是怎樣的

自定義校驗(yàn)注解

??我們以校驗(yàn)手機(jī)號碼為例,雖然validation提供了@Pattern這個注解來使用正則表達(dá)式進(jìn)行校驗(yàn)。如果被使用在多處,一旦正則表達(dá)式發(fā)生更改,則需要一個一個的進(jìn)行修改。很顯然為了避免做這樣的無用功,自定義校驗(yàn)注解就是你的好幫手。

@Data
public class PhoneForm {

    /**
     * 電話號碼
     */
    @Pattern(regexp = "^[1][3,4,5,7,8][0-9]{9}$" , message = "電話號碼有誤")
    private String phone;

}

??要實(shí)現(xiàn)一個自定義校驗(yàn)注解,主要是有兩步。一是注解本身,二是校驗(yàn)邏輯實(shí)現(xiàn)類

PhoneVerify 校驗(yàn)注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneValidator.class)
public @interface Phone {
 
    String message() default "手機(jī)號碼格式有誤";

    Class<?>[] groups() default {};

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

}
PhoneValidator 校驗(yàn)實(shí)現(xiàn)類
public class PhoneValidator implements ConstraintValidator<Phone, Object> {

    @Override
    public boolean isValid(Object telephone, ConstraintValidatorContext constraintValidatorContext) {
        String pattern = "^1[3|4|5|7|8]\\d{9}$";
        return Pattern.matches(pattern, telephone.toString());
    }
}
CustomForm 表單數(shù)據(jù)
@Data
public class CustomForm {

    /**
     * 電話號碼
     */
    @Phone
    private String phone;

}
測試接口
@PostMapping("/customTest")
public ResultVo customTest(@RequestBody @Validated CustomForm form){
	return ResultVoUtil.success(form.getPhone());
}
注解的含義
@Target({ElementType.FIELD})

??注解是指定當(dāng)前自定義注解可以使用在哪些地方,這里僅僅讓他可以使用屬性上。但還可以使用在更多的地方,比如說方法上、構(gòu)造器上等等。

  • TYPE - 類,接口(包括注解類型)或枚舉

  • FIELD - 字段(包括枚舉常量)

  • METHOD - 方法

  • PARAMETER - 參數(shù)

  • CONSTRUCTOR - 構(gòu)造函數(shù)

  • LOCAL_VARIABLE - 局部變量

  • ANNOTATION_TYPE -注解類型

  • PACKAGE - 包

  • TYPE_PARAMETER - 類型參數(shù)

  • TYPE_USE - 使用類型

@Retention(RetentionPolicy.RUNTIME)

??指定當(dāng)前注解保留到運(yùn)行時。保留策略有下面三種:

  • SOURCE - 注解只保留在源文件,當(dāng)Java文件編譯成class文件的時候,注解被遺棄。

  • CLASS - 注解被保留到class文件,但jvm加載class文件時候被遺棄,這是默認(rèn)的生命周期。

  • RUNTIME - 注解不僅被保存到class文件中,jvm加載class文件之后,仍然存在。

@Constraint(validatedBy = PhoneValidator.class)

??指定了當(dāng)前注解使用哪個校驗(yàn)類來進(jìn)行校驗(yàn)。

分組校驗(yàn)

UserForm
@Data
public class UserForm {

    /**
     * id
     */
    @Null(message = "新增時id必須為空", groups = {Insert.class})
    @NotNull(message = "更新時id不能為空", groups = {Update.class})
    private String id;

    /**
     * 類型
     */
    @NotEmpty(message = "姓名不能為空" , groups = {Insert.class})
    private String name;

    /**
     * 年齡
     */
    @NotEmpty(message = "年齡不能為空" , groups = {Insert.class})
    private String age;
    
}
Insert分組
public interface Insert {
}
Update分組
public interface Update {
}
測試接口
/**
 * 添加用戶
 */
@PostMapping("/addUser")
public ResultVo addUser(@RequestBody @Validated({Insert.class}) UserForm form){
  	// 選擇對應(yīng)的分組進(jìn)行校驗(yàn)
    return ResultVoUtil.success(form);
}

/**
 * 更新用戶
 */
@PostMapping("/updateUser")
public ResultVo updateUser(@RequestBody @Validated({Update.class}) UserForm form){
    // 選擇對應(yīng)的分組進(jìn)行校驗(yàn)
    return ResultVoUtil.success(form);
}
測試結(jié)果
添加測試

SpringBoot分組校驗(yàn)及自定義校驗(yàn)注解是怎樣的

更新測試

SpringBoot分組校驗(yàn)及自定義校驗(yàn)注解是怎樣的

順序校驗(yàn)@GroupSequence

??在@GroupSequence內(nèi)可以指定,分組校驗(yàn)的順序。比如說@GroupSequence({Insert.class, Update.class, UserForm.class})先執(zhí)行Insert校驗(yàn),然后執(zhí)行Update校驗(yàn)。如果Insert分組,校驗(yàn)失敗了,則不會進(jìn)行Update分組的校驗(yàn)。

@Data
@GroupSequence({Insert.class, Update.class, UserForm.class})
public class UserForm {

    /**
     * id
     */
    @Null(message = "新增時id必須為空", groups = {Insert.class})
    @NotNull(message = "更新時id不能為空", groups = {Update.class})
    private String id;

    /**
     * 類型
     */
    @NotEmpty(message = "姓名不能為空" , groups = {Insert.class})
    private String name;

    /**
     * 年齡
     */
    @NotEmpty(message = "年齡不能為空" , groups = {Insert.class})
    private String age;

}
測試接口
/**
* 編輯用戶
*/
@PostMapping("/editUser")
public ResultVo editUser(@RequestBody @Validated UserForm form){
	return ResultVoUtil.success(form);
}
測試結(jié)果

??哈哈哈,測試結(jié)果其實(shí)是個死循環(huán),不管你咋輸入都會報錯,小伙伴可以嘗試一下哦。上面的例子只是個演示,在實(shí)際中還是別這樣做了,需要根據(jù)具體邏輯進(jìn)行校驗(yàn)。

自定義分組校驗(yàn)

??對于之前提到了當(dāng)xxType值為A,paramA值必傳。xxType值為B,paramB值必須傳這樣的場景。單獨(dú)使用分組校驗(yàn)和分組序列是無法實(shí)現(xiàn)的。需要使用@GroupSequenceProvider才行。

自定義分組表單
@Data
@GroupSequenceProvider(value = CustomSequenceProvider.class)
public class CustomGroupForm {

    /**
     * 類型
     */
    @Pattern(regexp = "[A|B]" , message = "類型不必須為 A|B")
    private String type;

    /**
     * 參數(shù)A
     */
    @NotEmpty(message = "參數(shù)A不能為空" , groups = {WhenTypeIsA.class})
    private String paramA;

    /**
     * 參數(shù)B
     */
    @NotEmpty(message = "參數(shù)B不能為空", groups = {WhenTypeIsB.class})
    private String paramB;

    /**
     * 分組A
     */
    public interface WhenTypeIsA {

    }

    /**
     * 分組B
     */
    public interface WhenTypeIsB {

    }

}
CustomSequenceProvider
public class CustomSequenceProvider implements DefaultGroupSequenceProvider<CustomGroupForm> {

    @Override
    public List<Class<?>> getValidationGroups(CustomGroupForm form) {
        List<Class<?>> defaultGroupSequence = new ArrayList<>();

        defaultGroupSequence.add(CustomGroupForm.class);

        if (form != null && "A".equals(form.getType())) {
            defaultGroupSequence.add(CustomGroupForm.WhenTypeIsA.class);
        }

        if (form != null && "B".equals(form.getType())) {
            defaultGroupSequence.add(CustomGroupForm.WhenTypeIsB.class);
        }

        return defaultGroupSequence;
    }
}
測試接口
/**
 * 自定義分組
 */
@PostMapping("/customGroup")
public ResultVo customGroup(@RequestBody @Validated CustomGroupForm form){
    return ResultVoUtil.success(form);
}
測試結(jié)果
Type類型為A

SpringBoot分組校驗(yàn)及自定義校驗(yàn)注解是怎樣的

Type類型為B

SpringBoot分組校驗(yàn)及自定義校驗(yàn)注解是怎樣的

小結(jié)一下

??GroupSequence注解是一個標(biāo)準(zhǔn)的Bean認(rèn)證注解。正如之前,它能夠讓你靜態(tài)的重新定義一個類的,默認(rèn)校驗(yàn)組順序。然而GroupSequenceProvider它能夠讓你動態(tài)的定義一個校驗(yàn)組的順序。

注意的一個點(diǎn)

SpringBoot 2.3.x 移除了validation依賴需要手動引入依賴。

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

??個人的一些小經(jīng)驗(yàn),參數(shù)的非空判斷,這個應(yīng)該是校驗(yàn)的第一步了,除了非空校驗(yàn),我們還需要做到下面這幾點(diǎn):

  • 普通參數(shù) - 需要限定字段的長度。如果會將數(shù)據(jù)存入數(shù)據(jù)庫,長度以數(shù)據(jù)庫為準(zhǔn),反之根據(jù)業(yè)務(wù)確定。

  • 類型參數(shù) - 最好使用正則對可能出現(xiàn)的類型做到嚴(yán)格校驗(yàn)。比如type的值是【0|1|2】這樣的。

  • 列表(list)參數(shù) - 不僅需要對list內(nèi)的參數(shù)是否合格進(jìn)行校驗(yàn),還需要對list的size進(jìn)行限制。比如說 100。

  • 日期,郵件,金額,URL這類參數(shù)都需要使用對于的正則進(jìn)行校驗(yàn)。

  • 參數(shù)真實(shí)性 - 這個主要針對于 各種Id 比如說 userId、merchantId,對于這樣的參數(shù),都需要進(jìn)行真實(shí)性校驗(yàn),判斷系統(tǒng)內(nèi)是有含有,并且對應(yīng)的狀態(tài)是否正常。

??參數(shù)校驗(yàn)越嚴(yán)格越好,嚴(yán)格的校驗(yàn)規(guī)則不僅能減少接口出錯的概率,同時還能避免出現(xiàn)臟數(shù)據(jù),從而來保證系統(tǒng)的安全性和穩(wěn)定性。

關(guān)于SpringBoot分組校驗(yàn)及自定義校驗(yàn)注解是怎樣的就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI