欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot使用Validation校驗參數(shù)的詳細過程

 更新時間:2023年09月25日 15:38:58   作者:justry_deng  
這篇文章主要介紹了SpringBoot使用Validation校驗參數(shù),本文結(jié)合實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

JSR(Java Specification Requests)是Java界的重要標(biāo)準;JSR又細分很多標(biāo)準,其中JSR303就代表Bean Validation。更多細節(jié)可參考:https://jcp.org/en/jsr/detail?id=303。

準備工作

引入相關(guān)依賴:

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

注:本人測試時,還引入了lombok、SpringBoot的web、test等基礎(chǔ)依賴,這里就不一一給出了。

約束性注解(簡單)說明

注解

功能

@AssertFalse

可以為null,如果不為null的話必須為false

@AssertTrue

可以為null,如果不為null的話必須為true

@DecimalMax

設(shè)置不能超過最大值

@DecimalMin

設(shè)置不能超過最小值

@Digits

設(shè)置必須是數(shù)字且數(shù)字整數(shù)的位數(shù)和小數(shù)的位數(shù)必須在指定范圍內(nèi)

@Future

日期必須在當(dāng)前日期的未來

@Past

日期必須在當(dāng)前日期的過去

@Max

最大不得超過此最大值

@Min

最大不得小于此最小值

@NotNull

不能為null,可以是空

@Null

必須為null

@Pattern

必須滿足指定的正則表達式

@Size

集合、數(shù)組、map等的size()值必須在指定范圍內(nèi)

@Email

必須是email格式

@Length

長度必須在指定范圍內(nèi)

@NotBlank

字符串不能為null,字符串trim()后也不能等于“”

@NotEmpty

不能為null,集合、數(shù)組、map等size()不能為0;字符串trim()后可以等于“”

@Range

值必須在指定范圍內(nèi)

@URL

必須是一個URL

注:此表格只是簡單的對注解功能的說明,并沒有對每一個注解的屬性進行說明;可詳見源碼。

下面簡單測試一下上述各注解:

測試所用模型為:

import lombok.Getter;
import lombok.Setter;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.Range;
import org.hibernate.validator.constraints.URL;
import javax.validation.constraints.*;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
 * Validation注解
 *
 * @author JustryDeng
 * @date 2019/1/15 0:43
 */
public class ValidationBeanModel {
    @Setter
    @Getter
    public class AbcAssertFalse {
        @AssertFalse
        private Boolean myAssertFalse;
    }
    @Setter
    @Getter
    public class AbcAssertTrue {
        @AssertTrue
        private Boolean myAssertTrue;
    }
    @Setter
    @Getter
    public class AbcDecimalMax {
        @DecimalMax(value = "12.3")
        private String myDecimalMax;
    }
    @Setter
    @Getter
    public class AbcDecimalMin {
        @DecimalMin(value = "10.3")
        private String myDecimalMin;
    }
    @Setter
    @Getter
    public class AbcDigits {
        @Digits(integer = 5, fraction = 3)
        private Integer myDigits;
    }
    @Setter
    @Getter
    public class AbcEmail {
        @Email
        private String myEmail;
    }
    @Setter
    @Getter
    public class AbcFuture {
        @Future
        private Date myFuture;
    }
    @Setter
    @Getter
    public class AbcLength {
        @Length(min = 5, max = 10)
        private String myLength;
    }
    @Setter
    @Getter
    public class AbcMax {
        @Max(value = 200)
        private Long myMax;
    }
    @Setter
    @Getter
    public class AbcMin {
        @Min(value = 100)
        private Long myMin;
    }
    @Setter
    @Getter
    public class AbcNotBlank {
        @NotBlank
        private String myStringNotBlank;
        @NotBlank
        private String myObjNotBlank;
    }
    @Setter
    @Getter
    public class AbcNotEmpty {
        @NotEmpty
        private String myStringNotEmpty;
        @NotEmpty
        private String myNullNotEmpty;
        @NotEmpty
        private Map<String, Object> myMapNotEmpty;
        @NotEmpty
        private List<Object> myListNotEmpty;
        @NotEmpty
        private Object[] myArrayNotEmpty;
    }
    @Setter
    @Getter
    public class AbcNotNull {
        @NotNull
        private String myStringNotNull;
        @NotNull
        private Object myNullNotNull;
        @NotNull
        private Map<String, Object> myMapNotNull;
    }
    @Setter
    @Getter
    public class AbcNull {
        @Null
        private String myStringNull;
        @Null
        private Map<String, Object> myMapNull;
    }
    @Setter
    @Getter
    public class AbcPast {
        @Past
        private Date myPast;
    }
    @Setter
    @Getter
    public class AbcPattern {
        @Pattern(regexp = "\\d+")
        private String myPattern;
    }
    @Setter
    @Getter
    public class AbcRange {
        @Range(min = 100, max = 100000000000L)
        private Double myRange;
    }
    @Setter
    @Getter
    public class AbcSize {
        @Size(min = 3, max = 5)
        private List<Integer> mySize;
    }
    @Setter
    @Getter
    public class AbcURL {
        @URL
        private String myURL;
    }
}

測試方法為:

import com.aspire.model.*;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.*;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ValidationDemoApplicationTests {
    private Validator validator;
    @Before
    public void initValidator() {
        ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
        validator = validatorFactory.getValidator();
    }
    /**
     * 在myAssertTrue屬性上加@AssertTrue注解
     * <p>
     * 程序輸出:  com.aspire.model.ValidationBeanModel$AbcAssertTrue類的myAssertTrue屬性 -> 只能為true
     */
    @Test
    public void testAssertTrue() {
        ValidationBeanModel.AbcAssertTrue vm = new ValidationBeanModel().new AbcAssertTrue();
        vm.setMyAssertTrue(false);
        fa(vm);
    }
    /**
     * 在myAssertFalse屬性上加@AssertFalse注解
     * <p>
     * 程序輸出:  com.aspire.model.ValidationBeanModel$AbcAssertFalse類的myAssertFalse屬性 -> 只能為false
     */
    @Test
    public void testAssertFalse() {
        ValidationBeanModel.AbcAssertFalse vm = new ValidationBeanModel().new AbcAssertFalse();
        vm.setMyAssertFalse(true);
        fa(vm);
    }
    /**
     * 在myDecimalMax屬性上加@DecimalMax(value = "12.3")注解
     * <p>
     * 程序輸出:  com.aspire.model.ValidationBeanModel$AbcDecimalMax類的myDecimalMax屬性 -> 必須小于或等于12.3
     */
    @Test
    public void testDecimalMax() {
        ValidationBeanModel.AbcDecimalMax vm = new ValidationBeanModel().new AbcDecimalMax();
        vm.setMyDecimalMax("123");
        fa(vm);
    }
    /**
     * 在myDecimalMin屬性上加@DecimalMin(value = "10.3")注解
     * <p>
     * 程序輸出:  com.aspire.model.ValidationBeanModel$AbcDecimalMin類的myDecimalMin屬性 -> 必須大于或等于10.3
     */
    @Test
    public void testDecimalMin() {
        ValidationBeanModel.AbcDecimalMin vm = new ValidationBeanModel().new AbcDecimalMin();
        vm.setMyDecimalMin("1.23");
        fa(vm);
    }
    /**
     * 在myDigits屬性上加@Digits(integer = 5, fraction = 3)注解
     * <p>
     * 程序輸出:  com.aspire.model.ValidationBeanModel$AbcDigits類的myDigits屬性 -> 數(shù)字的值超出了允許范圍(只允許在5位整數(shù)和3位小數(shù)范圍內(nèi))
     */
    @Test
    public void testDigits() {
        ValidationBeanModel.AbcDigits vm = new ValidationBeanModel().new AbcDigits();
        vm.setMyDigits(1000738);
        fa(vm);
    }
    /**
     * 在myEmail屬性上加@Email注解
     * <p>
     * 程序輸出:  com.aspire.model.ValidationBeanModel$AbcEmail類的myEmail屬性 -> 不是一個合法的電子郵件地址
     */
    @Test
    public void testEmail() {
        ValidationBeanModel.AbcEmail vm = new ValidationBeanModel().new AbcEmail();
        vm.setMyEmail("asd@.com");
        fa(vm);
    }
    /**
     * 在myFuture屬性上加@Future注解
     * <p>
     * 程序輸出:  com.aspire.model.ValidationBeanModel$AbcFuture類的myFuture屬性 -> 需要是一個將來的時間
     */
    @Test
    public void testFuture() {
        ValidationBeanModel.AbcFuture vm = new ValidationBeanModel().new AbcFuture();
        vm.setMyFuture(new Date(10000L));
        fa(vm);
    }
    /**
     * 在myLength屬性上加@Length(min = 5, max = 10)注解
     * <p>
     * 程序輸出:  com.aspire.model.ValidationBeanModel$AbcLength類的myLength屬性 -> 長度需要在5和10之間
     */
    @Test
    public void testLength() {
        ValidationBeanModel.AbcLength vm = new ValidationBeanModel().new AbcLength();
        vm.setMyLength("abcd");
        fa(vm);
    }
    /**
     * 在myMax屬性上加@Max(value = 200)注解
     * <p>
     * 程序輸出:  com.aspire.model.ValidationBeanModel$AbcMax類的myMax屬性 -> 最大不能超過200
     */
    @Test
    public void testMax() {
        ValidationBeanModel.AbcMax vm = new ValidationBeanModel().new AbcMax();
        vm.setMyMax(201L);
        fa(vm);
    }
    /**
     * 在myMin屬性上加@Min(value = 200)注解
     * <p>
     * 程序輸出:  com.aspire.model.ValidationBeanModel$AbcMin類的myMin屬性 -> 最小不能小于100
     */
    @Test
    public void testMin() {
        ValidationBeanModel.AbcMin vm = new ValidationBeanModel().new AbcMin();
        vm.setMyMin(99L);
        fa(vm);
    }
    /**
     * 在myStringNotBlank屬性上加@NotBlank注解
     * 在myObjNotBlank屬性上加@NotBlank注解
     *
     * 注:如果屬性值為null 或者 .trim()后等于"",那么會提示 不能為空
     * <p>
     * 程序輸出:   com.aspire.model.ValidationBeanModel$AbcNotBlank類的myObjNotBlank屬性 -> 不能為空
     *            com.aspire.model.ValidationBeanModel$AbcNotBlank類的myStringNotBlank屬性 -> 不能為空
     */
    @Test
    public void testNotBlank() {
        ValidationBeanModel.AbcNotBlank vm = new ValidationBeanModel().new AbcNotBlank();
        vm.setMyObjNotBlank(null);
        vm.setMyStringNotBlank(" ");
        fa(vm);
    }
    /**
     * 在myStringNotEmpty屬性上加@NotEmpty注解
     * 在myNullNotEmpty屬性上加@NotEmpty注解
     * 在myMapNotEmpty屬性上加@NotEmpty注解
     * 在myListNotEmpty屬性上加@NotEmpty注解
     * 在myArrayNotEmpty屬性上加@NotEmpty注解
     *
     * 注:String可以是.trim()后等于""的字符串,但是不能為null
     * 注:MAP、Collection、Array既不能是空,也不能是null
     *
     * <p>
     * 程序輸出: com.aspire.model.ValidationBeanModel$AbcNotEmpty類的myNullNotEmpty屬性 -> 不能為空
     *           com.aspire.model.ValidationBeanModel$AbcNotEmpty類的myListNotEmpty屬性 -> 不能為空
     *           com.aspire.model.ValidationBeanModel$AbcNotEmpty類的myArrayNotEmpty屬性 -> 不能為空
     *           com.aspire.model.ValidationBeanModel$AbcNotEmpty類的myMapNotEmpty屬性 -> 不能為空
     */
    @Test
    public void testNotEmpty() {
        ValidationBeanModel.AbcNotEmpty vm = new ValidationBeanModel().new AbcNotEmpty();
        vm.setMyStringNotEmpty(" ");
        vm.setMyNullNotEmpty(null);
        vm.setMyMapNotEmpty(new HashMap<>(0));
        vm.setMyListNotEmpty(new ArrayList<>(0));
        vm.setMyArrayNotEmpty(new String[]{});
        fa(vm);
    }
    /**
     * 在myStringNotNull屬性上加@NotNull注解
     * 在myNullNotNull屬性上加@NotNull注解
     * 在myMapNotNull屬性上加@NotNull注解
     *
     * 注:屬性值可以是空的, 但是就是不能為null
     * <p>
     * 程序輸出:   com.aspire.model.ValidationBeanModel$AbcNotNull類的myNullNotNull屬性 -> 不能為null
     */
    @Test
    public void testNotNull() {
        ValidationBeanModel.AbcNotNull vm = new ValidationBeanModel().new AbcNotNull();
        vm.setMyStringNotNull("   ");
        vm.setMyNullNotNull(null);
        vm.setMyMapNotNull(new HashMap<>(0));
        fa(vm);
    }
    /**
     * 在myStringNull屬性上加@Null注解
     * 在myMapNotNull屬性上加@Null注解
     *
     * 注:屬性值必須是null, 是空都不行
     * <p>
     * 程序輸出:   com.aspire.model.ValidationBeanModel$AbcNull類的myMapNull屬性 -> 必須為null
     *            com.aspire.model.ValidationBeanModel$AbcNull類的myStringNull屬性 -> 必須為null
     */
    @Test
    public void testNull() {
        ValidationBeanModel.AbcNull vm = new ValidationBeanModel().new AbcNull();
        vm.setMyStringNull("   ");
        vm.setMyMapNull(new HashMap<>(0));
        fa(vm);
    }
    /**
     * 在myPast屬性上加@Past注解
     *
     * <p>
     * 程序輸出:   com.aspire.model.ValidationBeanModel$AbcPast類的myPast屬性 -> 需要是一個過去的時間
     */
    @Test
    public void testPast() {
        ValidationBeanModel.AbcPast vm = new ValidationBeanModel().new AbcPast();
        vm.setMyPast(new Date(20000000000000000L));
        fa(vm);
    }
    /**
     * 在myPattern屬性上加@Pattern(regexp = "\\d+")注解
     *
     * <p>
     * 程序輸出:   com.aspire.model.ValidationBeanModel$AbcPattern類的myPattern屬性 -> 需要匹配正則表達式"\d"
     */
    @Test
    public void testPattern() {
        ValidationBeanModel.AbcPattern vm = new ValidationBeanModel().new AbcPattern();
        vm.setMyPattern("ABC");
        fa(vm);
    }
    /**
     * 在myRange屬性上加@Range(min = 100, max = 100000000000L)注解
     *
     * <p>
     * 程序輸出:   com.aspire.model.ValidationBeanModel$AbcRange類的myRange屬性 -> 需要在100和100000000000之間
     */
    @Test
    public void testRange() {
        ValidationBeanModel.AbcRange vm = new ValidationBeanModel().new AbcRange();
        vm.setMyRange(32222222222222222222222222222222.323);
        fa(vm);
    }
    /**
     * 在mySize屬性上加@Size(min = 3, max = 5)注解
     *
     * <p>
     * 程序輸出:   com.aspire.model.ValidationBeanModel$AbcSize類的mySize屬性 -> 個數(shù)必須在3和5之間
     */
    @Test
    public void testSize() {
        ValidationBeanModel.AbcSize vm = new ValidationBeanModel().new AbcSize();
        List<Integer> list = new ArrayList<>(4);
        list.add(0);
        list.add(1);
        vm.setMySize(list);
        fa(vm);
    }
    /**
     * 在myURL屬性上加@URL注解
     *
     * <p>
     * 程序輸出:   com.aspire.model.ValidationBeanModel$AbcURL類的myURL屬性 -> 需要是一個合法的URL
     */
    @Test
    public void testURL() {
        ValidationBeanModel.AbcURL vm = new ValidationBeanModel().new AbcURL();
        vm.setMyURL("www.baidu.xxx");
        fa(vm);
    }
    private <T> void fa(T obj) {
        Set<ConstraintViolation<T>> cvSet = validator.validate(obj);
        for (ConstraintViolation<T> cv : cvSet) {
            System.err.println(cv.getRootBean().getClass().getName() + "類的"
                    + cv.getPropertyPath() + "屬性 -> " + cv.getMessage());
        }
    }
}

@Validated的使用時機

@Validated的使用位置較多(可詳見源碼),但其主流的使用位置卻是以下兩種:

1、在Controller層中,放在模型參數(shù)對象前。         

當(dāng)Controller層中參數(shù)是一個對象模型時,只有將@Validated直接放在該模型前,該模型內(nèi)部的字段才會被   

校驗(如果有對該模型的字段進行約束的話)。

2、在Controller層中,放在類上。           

當(dāng)一些約束是直接出現(xiàn)在Controller層中的參數(shù)前時,只有將@Validated放在類上時,參數(shù)前的約束才會生效。

以下是簡單的測試代碼:

import com.aspire.model.ValidationBeanModel;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.DecimalMax;
/**
 * Controller層 --- 初步簡單測試 @Validated 的使用位置
 *
 * 對比測試過程:
 *    方案一 : 不在類上加@Validated注解,訪問這六個接口
 *    方案二 : 在類上加@Validated注解,再次訪問這六個接口
 *
 *    對比方案一和方案二,可初步得出@Validated的使用時機:
 *        1.當(dāng)我們是在模型里面對模型字段添加約束注解,在Controller中使用模型接收數(shù)
 *          據(jù)時,@Validated要直接放在該模型參數(shù)前才有效。 如: "/test/one"
 *        2.當(dāng)我們是直接在Controller層中的參數(shù)前,使用約束注解時,@Validated要直接放在類上,
 *          才會有效。如: /test/six
 *
 *
 * @author JustryDeng
 * @date 2019/1/18 22:22
 */
@RestController
@Validated
public class JustryDengController {
    @RequestMapping(value = "/test/one")
    public String validatioOne(@Validated ValidationBeanModel.AbcDecimalMax myDecimalMax) {
        System.out.println(myDecimalMax.getMyDecimalMax());
        return "one pass!";
    }
    @RequestMapping(value = "/test/two")
    @Validated
    public String validatioTwo(ValidationBeanModel.AbcDecimalMax myDecimalMax) {
        System.out.println(myDecimalMax.getMyDecimalMax());
        return "two pass!";
    }
    @RequestMapping(value = "/test/three")
    public String validatioThree(ValidationBeanModel.AbcDecimalMax myDecimalMax) {
        System.out.println(myDecimalMax.getMyDecimalMax());
        return "three pass!";
    }
    @RequestMapping(value = "/test/four")
    public String validatioFour(@Validated  @DecimalMax(value = "12.3") String myDecimalMax) {
        System.out.println(myDecimalMax);
        return "four pass!";
    }
    @RequestMapping(value = "/test/five")
    @Validated
    public String validatioFive(@DecimalMax(value = "12.3") String myDecimalMax) {
        System.out.println(myDecimalMax);
        return "five pass!";
    }
    @RequestMapping(value = "/test/six")
    @Validated
    public String validatioSix(@DecimalMax(value = "12.3") String myDecimalMax) {
        System.out.println(myDecimalMax);
        return "six pass!";
    }
}

@Validated與@Valid的簡單對比說明

        @Valid注解與@Validated注解功能大部分類似;兩者的不同主要在于:@Valid屬于javax下的,而@Validated屬于spring下;@Valid支持嵌套校驗、而@Validated不支持,@Validated支持分組,而@Valid不支持。筆者這里只簡單介紹@Validated的使用時機。

自定義注解

        雖然Bean Validation和Hibernate Validator已經(jīng)提供了非常豐富的校驗注解,但是在實際業(yè)務(wù)中,難免會碰到一些現(xiàn)有注解不足以校驗的情況;這時,我們可以考慮自定義Validation注解。

示例:

第一步:創(chuàng)建自定義注解

import com.aspire.constraints.impl.JustryDengValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
 * 自定義校驗注解
 * 提示:
 *     1、message、contains、payload是必須要寫的
 *     2、還需要什么方法可根據(jù)自己的實際業(yè)務(wù)需求,自行添加定義即可
 *
 * 注:當(dāng)沒有指定默認值時,那么在使用此注解時,就必須輸入對應(yīng)的屬性值
 *
 * @author JustryDeng
 * @date 2019/1/15 1:17
 */
@Target({FIELD, PARAMETER})
@Retention(RUNTIME)
@Documented
// 指定此注解的實現(xiàn),即:驗證器
@Constraint(validatedBy ={JustryDengValidator.class})
public @interface ConstraintsJustryDeng {
    // 當(dāng)驗證不通過時的提示信息
    String message() default "JustryDeng : param value must contais specified value!";
    // 根據(jù)實際需求定的方法
    String contains() default "";
    // 約束注解在驗證時所屬的組別
    Class<?>[] groups() default { };
    // 負載
    Class<? extends Payload>[] payload() default { };
}

第二步:編寫(第一步中的校驗器實現(xiàn)類)該注解

import com.aspire.constraints.anno.ConstraintsJustryDeng;
import org.hibernate.validator.internal.engine.ValidationContext;
import org.hibernate.validator.internal.engine.ValueContext;
import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
 * ConstraintsJustryDeng注解 校驗器 實現(xiàn)
 * <p>
 * 注:驗證器需要實現(xiàn)ConstraintValidator<U, V>, 其中 U為對應(yīng)的注解類, V為被該注解標(biāo)記的字段的類型(或其父類型)
 *
 * 注: 當(dāng)項目啟動后,會(懶加載)創(chuàng)建ConstraintValidator實例,在創(chuàng)建實例后會初始化調(diào)
 *     用{@link ConstraintValidator#initialize}方法。
 *     所以, 只有在第一次請求時,會走initialize方法, 后面的請求是不會走initialize方法的。
 *
 * 注: (懶加載)創(chuàng)建ConstraintValidator實例時, 會走緩存; 如果緩存中有,則直接使用相
 *     同的ConstraintValidator實例; 如果緩存中沒有,那么會創(chuàng)建新的ConstraintValidator實例。
 *     由于緩存的key是能唯一定位的, 且 ConstraintValidator的實例屬性只有在
 *     {@link ConstraintValidator#initialize}方法中才會寫;在{@link ConstraintValidator#isValid}
 *     方法中只是讀。
 *     所以不用擔(dān)心線程安全問題。
 *
 * 注: 如何創(chuàng)建ConstraintValidator實例的,可詳見源碼
 *     @see ConstraintTree#getInitializedConstraintValidator(ValidationContext, ValueContext)
 *
 * @author JustryDeng
 * @date 2019/1/15 1:19
 */
public class JustryDengValidator implements ConstraintValidator<ConstraintsJustryDeng, Object> {
    /** 錯誤提示信息 */
    private String contains;
    /**
     * 初始化方法, 在(懶加載)創(chuàng)建一個當(dāng)前類實例后,會馬上執(zhí)行此方法
     *
     * 注: 此方法只會執(zhí)行一次,即:創(chuàng)建實例后馬上執(zhí)行。
     *
     * @param constraintAnnotation
     *         注解信息模型,可以從該模型中獲取注解類中定義的一些信息,如默認值等
     * @date 2019/1/19 11:27
     */
    @Override
    public void initialize(ConstraintsJustryDeng constraintAnnotation) {
        System.out.println(constraintAnnotation.message());
        this.contains = constraintAnnotation.contains();
    }
    /**
     * 校驗方法, 每個需要校驗的請求都會走這個方法
     *
     * 注: 此方法可能會并發(fā)執(zhí)行,需要根據(jù)實際情況看否是需要保證線程安全。
     *
     * @param value
     *         被校驗的對象
     * @param context
     *         上下文
     *
     * @return 校驗是否通過
     */
    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        if (value == null) {
            return false;
        }
        if (value instanceof String) {
            String strMessage = (String) value;
            return strMessage.contains(contains);
        } else if (value instanceof Integer) {
            return contains.contains(String.valueOf(value));
        }
        return false;
    }
}

第三步:自定義注解簡單使用測試

Controller層中是這樣的:

訪問一下這個接口(當(dāng)參數(shù)值符合要求時):

訪問一下這個接口(當(dāng)參數(shù)值不符合要求時):

對注解拋出的異常進行處理

說明:當(dāng)注解校驗不通過時,直接將異常信息返回給前端其實并不友好,我們可以將異常包裝一下,返回給前端。

情況一:使用BindingResult類來容納異常信息,當(dāng)校驗不通過時,不影響正常程               

序往下走。我們只需要處理BindingResult中的異常信息即可。

處理前:

參數(shù)模型是這樣的:

Controller是這樣的:

使用postman測試,當(dāng)校驗不通過時顯示如下:

處理后:

參數(shù)模型是這樣的(沒有變):

Controller是這樣的(在@Validated注解的參數(shù)后,緊接著加上BindingResult)

再次使用postman測試,當(dāng)校驗不通過時顯示如下:

postman中返回了數(shù)據(jù),說明雖然參數(shù)錯誤,但是不影響程序的執(zhí)行。

程序在控制臺輸出了如下信息:

可見,后臺已經(jīng)獲悉了錯誤,至于怎么處理,這就看偉大的程序員們了。

情況二(推薦):通過SpringMVC全局異常處理器來處理異常。

描述:如果不采用BindingResult來容納異常信息時,那么異常會被向外拋出。注解校驗不通過時,可能拋出的           異常有BindException異常、ValidationException異常(或其子類異常)、           MethodArgumentNotValidException異常。

處理前:

示例一:Controller和對應(yīng)的參數(shù)模型是這樣的:

使用postman測試,當(dāng)校驗不通過時顯示如下:

示例二:Controller是這樣的:

使用postman測試,當(dāng)校驗不通過時顯示如下:

注:ConstraintViolationException異常是ViolationException異常的子異常。

示例三:Controller是這樣的:

注:此處的User模型與情況一里給出的模型是一樣的,這里就不再給出了。

使用postman測試,當(dāng)校驗不通過時顯示如下:

進行處理:加入AOP全局異常處理器:

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.util.HashMap;
import java.util.Map;
/**
 * SpringMVC統(tǒng)一異常處理
 *
 * @author JustryDeng
 * @date 2019/10/12 16:28
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
	/**
	 * 處理Validated校驗異常
	 * <p>
	 * 注: 常見的ConstraintViolationException異常, 也屬于ValidationException異常
	 *
	 * @param e
	 *         捕獲到的異常
	 * @return 返回給前端的data
	 */
	@ResponseStatus(code = HttpStatus.BAD_REQUEST)
	@ExceptionHandler(value = {BindException.class, ValidationException.class, MethodArgumentNotValidException.class})
	public Map<String, Object> handleParameterVerificationException(Exception e) {
		log.error(" handleParameterVerificationException has been invoked", e);
		Map<String, Object> resultMap = new HashMap<>(4);
		resultMap.put("code", "100001");
		String msg = null;
		if (e instanceof MethodArgumentNotValidException) {
			BindingResult bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
			// getFieldError獲取的是第一個不合法的參數(shù)(P.S.如果有多個參數(shù)不合法的話)
			FieldError fieldError = bindingResult.getFieldError();
			if (fieldError != null) {
				msg = fieldError.getDefaultMessage();
			}
		} else if (e instanceof BindException) {
			// getFieldError獲取的是第一個不合法的參數(shù)(P.S.如果有多個參數(shù)不合法的話)
			FieldError fieldError = ((BindException) e).getFieldError();
			if (fieldError != null) {
				msg = fieldError.getDefaultMessage();
			}
		} else if (e instanceof ConstraintViolationException) {
			/*
			 * ConstraintViolationException的e.getMessage()形如
			 *     {方法名}.{參數(shù)名}: {message}
			 *  這里只需要取后面的message即可
			 */
			msg = e.getMessage();
			if (msg != null) {
				int lastIndex = msg.lastIndexOf(':');
				if (lastIndex >= 0) {
					msg = msg.substring(lastIndex + 1).trim();
				}
			}
			/// ValidationException 的其它子類異常
		} else {
			msg = "處理參數(shù)時異常";
		}
		resultMap.put("msg", msg);
		return resultMap;
	}
}

處理后,使用postman再次進行上述兩個請求:

可見,異常處理成功!

【補充】多級嵌套模型的校驗

假設(shè):

有api:

有Company和Employee類:

那么,當(dāng)Department里面既有普通字段,又有類字段時:

解決方式是,給復(fù)雜對象字段加上@Valid注解:

【補充】groups分組校驗

       在很多時候,同一個模型可能會在多處被用到,但每處的校驗場景又不一定相同(如:新增用戶接口、修改用戶接口,參數(shù)都是User模型,在新增時User中name字段不能為空,userNo字段可以為空;在修改時User中name字段可以為空,userNo字段不能為空)。我們可以用groups來實現(xiàn):同一個模型在不同場景下,(動態(tài)區(qū)分)校驗?zāi)P椭械牟煌侄巍?/p>

提示:實現(xiàn)groups功能的主要邏輯源碼,可詳見org.hibernate.validator.internal.engine.ValidatorImpl#validate。

使用方式(示例說明)

準備工作:自定義兩個分組。

提示:繼承Default并不是必須的。只是說,如果繼承了Default,那么@Validated(value = Create.class)的校驗范疇就           

為【Create】和【Default】;

如果沒繼承Default,那么@Validated(value = Create.class)的校驗范疇只         

 為【Create】,而@Validated(value = {Create.class, Default.class})的校驗范疇才為【Create】和【Default】。           

追注:原因可見下面的第二步中的說明。

第一步:(在給模型里面的參數(shù)定義校驗規(guī)則時,)給校驗分配所屬分組。

注:Default組和無參構(gòu)造機制類似,當(dāng)沒有指定分組時,會默認當(dāng)前校驗屬于Default組,但是一旦主動給當(dāng)前校驗指定       

了分組(如上圖中的name字段,主動指定了屬于Create組),那么就不會再額外指定屬于Default組了。        

追注:當(dāng)然,也可以畫蛇添足的主動指定所屬分組為Default。

第二步:啟動校驗時,指定校驗(組別)范疇。

為更直觀的理解,再給出一張圖:

注:還有一個用于聯(lián)合校驗(如:字段A的校驗,依賴于字段B的校驗)等場景的注解javax.validation.GroupSequence       有時也非常實用,感興趣的可自行了解。

到此這篇關(guān)于SpringBoot使用Validation校驗參數(shù)的文章就介紹到這了,更多相關(guān)SpringBoot使用Validation校驗參數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java8中Optional操作的實際應(yīng)用

    Java8中Optional操作的實際應(yīng)用

    Optional類是一個可以為null的容器對象,如果值存在則isPresent()方法會返回true,調(diào)用get()方法會返回該對象,下面這篇文章主要給大家介紹了關(guān)于Java8中Optional操作實際應(yīng)用的相關(guān)資料,需要的朋友可以參考下
    2022-02-02
  • 使用SpringBoot配置虛擬化路徑用于圖片的展示

    使用SpringBoot配置虛擬化路徑用于圖片的展示

    這篇文章主要介紹了使用SpringBoot配置虛擬化路徑用于圖片的展示方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • Spring Data JPA例子代碼[基于Spring Boot、Mysql]

    Spring Data JPA例子代碼[基于Spring Boot、Mysql]

    這篇文章主要介紹了Spring Data JPA例子代碼[基于Spring Boot、Mysql],小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04
  • Mybatis-plus分頁查詢不生效問題排查全過程

    Mybatis-plus分頁查詢不生效問題排查全過程

    最近寫分頁的時候,遇到了分頁無法正常發(fā)揮作用的問題,下面這篇文章主要給大家介紹了關(guān)于Mybatis-plus分頁查詢不生效問題排查的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-03-03
  • SpringBoot整合MongoDB實現(xiàn)文件上傳下載刪除

    SpringBoot整合MongoDB實現(xiàn)文件上傳下載刪除

    這篇文章主要介紹了SpringBoot整合MongoDB實現(xiàn)文件上傳下載刪除的方法,幫助大家更好的理解和學(xué)習(xí)使用SpringBoot框架,感興趣的朋友可以了解下
    2021-05-05
  • SpringBoot如何加載多個YAML配置文件

    SpringBoot如何加載多個YAML配置文件

    在Spring?Boot中加載多個?YAML?配置文件是一個常見的需求,通常用于將配置信息分離到多個文件中以便于管理和維護,下面就跟隨小編一起來看看具體實現(xiàn)吧
    2025-02-02
  • java編程創(chuàng)建型設(shè)計模式單例模式的七種示例

    java編程創(chuàng)建型設(shè)計模式單例模式的七種示例

    這篇文章主要為大家介紹了java編程中創(chuàng)建型設(shè)計模式之單例模式的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2022-02-02
  • SpringBoot異步使用@Async的原理以及線程池配置詳解

    SpringBoot異步使用@Async的原理以及線程池配置詳解

    在項目中當(dāng)訪問其他人的接口較慢時,不想程序一直卡在耗時任務(wù)上,想程序能夠并行執(zhí)行,我們可以使用多線程來并行的處理任務(wù),也可以使用spring提供的異步處理方式@Async,這篇文章主要給大家介紹了關(guān)于SpringBoot異步使用@Async的原理以及線程池配置的相關(guān)資料
    2021-09-09
  • 詳解Java如何判斷一個對象是否為空

    詳解Java如何判斷一個對象是否為空

    我們在剛開始學(xué)習(xí)Java的時候,遇到過最多的異常肯定是臭名昭著的空指針異常(NullPointerException),可以說它陪伴了我們整個初學(xué)階段,那么如何優(yōu)雅的判斷一個對象是否為空并且減少空指針異常呢,
    2024-01-01
  • spring boot動態(tài)加載Echart餅狀圖

    spring boot動態(tài)加載Echart餅狀圖

    這篇文章主要為大家詳細介紹了spring boot動態(tài)加載Echart餅狀圖,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-12-12

最新評論