如何優(yōu)雅的做數(shù)據(jù)校驗(yàn)-Hibernate Validator詳細(xì)使用說(shuō)明

數(shù)據(jù)校驗(yàn)是在平時(shí)的編碼過程中常做的工作月杉,在系統(tǒng)的各個(gè)層可能都要去實(shí)現(xiàn)一些校驗(yàn)邏輯雾棺,再去做業(yè)務(wù)處理。這些繁瑣的校驗(yàn)與我們的業(yè)務(wù)代碼在一塊就會(huì)顯得臃腫掠廓。而且這些校驗(yàn)通常是業(yè)務(wù)無(wú)關(guān)的简烘。也是在工作中使用到Hibernate Validator苔严,但卻發(fā)現(xiàn)有人沒有使用好它(竟然還能看到一些if else的校驗(yàn)代碼...),所以在這里決定整理下關(guān)于Hibernate Validator的使用

Bean Validation 2.0(JSR 380)定義了用于實(shí)體和方法驗(yàn)證的元數(shù)據(jù)模型和API孤澎,Hibernate Validator是目前最好的實(shí)現(xiàn)届氢,這篇主要是說(shuō)Hibernate Validator的使用

Hibernate Validator的使用

依賴

如果是Spring Boot項(xiàng)目,那么spring-boot-starter-web中就已經(jīng)依賴hibernate-validator

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

如果是Spring Mvc覆旭,那可以直接添加hibernate-validator依賴

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.17.Final</version>
</dependency>

bean約束聲明和驗(yàn)證退子,Validator

先給我們的Java對(duì)象添加約束注解

@Data
@AllArgsConstructor
public class User {

    private String id;

    @NotBlank
    @Size(max = 20)
    private String name;

    @NotNull
    @Pattern(regexp = "[A-Z][a-z][0-9]")
    private String password;
    
    @NotNull
    private Integer age;

    @Max(10)
    @Min(1)
    private Integer level;
}

驗(yàn)證實(shí)體實(shí)例需要先獲取Validator實(shí)例

ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();

Validator接口有三個(gè)方法,可用于驗(yàn)證整個(gè)實(shí)體或僅驗(yàn)證實(shí)體的單個(gè)屬性

  • Validator#validate() 驗(yàn)證所有bean的所有約束
  • Validator#validateProperty() 驗(yàn)證單個(gè)屬性
  • Validator#validateValue() 檢查給定類的單個(gè)屬性是否可以成功驗(yàn)證
public class UserTest {

    private static Validator validator;

    @BeforeAll
    public static void setUpValidator() {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();
    }

    @Test
    public void validatorTest() {
        User user = new User(null, "", "!@#$", null, 11);

        // 驗(yàn)證所有bean的所有約束
        Set<ConstraintViolation<User>> constraintViolations = validator.validate(user);
        // 驗(yàn)證單個(gè)屬性
        Set<ConstraintViolation<User>> constraintViolations2 = validator.validateProperty(user, "name");
        // 檢查給定類的單個(gè)屬性是否可以成功驗(yàn)證
        Set<ConstraintViolation<User>> constraintViolations3 = validator.validateValue(User.class, "password", "sa!");

        constraintViolations.forEach(constraintViolation -> System.out.println(constraintViolation.getMessage()));
        constraintViolations2.forEach(constraintViolation -> System.out.println(constraintViolation.getMessage()));
        constraintViolations3.forEach(constraintViolation -> System.out.println(constraintViolation.getMessage()));
    }
}

測(cè)試結(jié)果

不能為空
最大不能超過10
需要匹配正則表達(dá)式"[A-Z][a-z][0-9]"
不能為null
不能為空
需要匹配正則表達(dá)式"[A-Z][a-z][0-9]"

方法約束聲明和驗(yàn)證型将,ExecutableValidator

從Bean Validation 1.1開始寂祥,約束不僅可以應(yīng)用于JavaBean及其屬性,而且可以應(yīng)用于任何Java類型的方法和構(gòu)造函數(shù)的參數(shù)和返回值七兜,這里簡(jiǎn)單看一個(gè)例子

public class RentalStation {

    public RentalStation(@NotNull String name) {
        //...
    }

    public void rentCar(@NotNull @Future LocalDate startDate, @Min(1) int durationInDays) {
        //...
    }

    @NotNull
    @Size(min = 1)
    public List<@NotNull String> getCustomers() {
        //...
        return null;
    }
}

ExecutableValidator接口可以完成方法約束的驗(yàn)證

ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
executableValidator = factory.getValidator().forExecutables();

ExecutableValidator界面共有四種方法:

  • validateParameters()validateReturnValue()用于方法驗(yàn)證
  • validateConstructorParameters()validateConstructorReturnValue()用于構(gòu)造函數(shù)驗(yàn)證
public class RentalStationTest {

    private static ExecutableValidator executableValidator;

    @BeforeAll
    public static void setUpValidator() {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        executableValidator = factory.getValidator().forExecutables();
    }

    @Test
    public void validatorTest() throws NoSuchMethodException {
        RentalStation rentalStation = new RentalStation("z");

        Method method = RentalStation.class.getMethod("rentCar", LocalDate.class, int.class);
        Object[] parameterValues = {LocalDate.now().minusDays(1), 0};
        Set<ConstraintViolation<RentalStation>> violations = executableValidator.validateParameters(
                rentalStation, method, parameterValues);

        violations.forEach(violation -> System.out.println(violation.getMessage()));
    }
}

測(cè)試結(jié)果

需要是一個(gè)將來(lái)的時(shí)間
最小不能小于1

約束注解

validator-api-2.0的約束注解有22個(gè)丸凭,具體我們看下面表格

空與非空檢查

注解 支持Java類型 說(shuō)明
@Null Object 為null
@NotNull Object 不為null
@NotBlank CharSequence 不為null,且必須有一個(gè)非空格字符
@NotEmpty CharSequence腕铸、Collection惜犀、Map、Array 不為null狠裹,且不為空(length/size>0)

Boolean值檢查

注解 支持Java類型 說(shuō)明 備注
@AssertTrue boolean虽界、Boolean 為true 為null有效
@AssertFalse boolean、Boolean 為false 為null有效

日期檢查

注解 支持Java類型 說(shuō)明 備注
@Future Date涛菠、Calendar莉御、Instant、LocalDate俗冻、LocalDateTime礁叔、LocalTime、MonthDay言疗、OffsetDateTime晴圾、OffsetTime颂砸、Year噪奄、YearMonth死姚、ZonedDateTime、HijrahDate勤篮、JapaneseDate都毒、MinguoDate、ThaiBuddhistDate 驗(yàn)證日期為當(dāng)前時(shí)間之后 為null有效
@FutureOrPresent Date碰缔、Calendar账劲、Instant、LocalDate金抡、LocalDateTime瀑焦、LocalTime、MonthDay梗肝、OffsetDateTime榛瓮、OffsetTime、Year巫击、YearMonth禀晓、ZonedDateTime、HijrahDate坝锰、JapaneseDate粹懒、MinguoDate、ThaiBuddhistDate 驗(yàn)證日期為當(dāng)前時(shí)間或之后 為null有效
@Past Date顷级、Calendar凫乖、Instant、LocalDate弓颈、LocalDateTime拣凹、LocalTime、MonthDay恨豁、OffsetDateTime忘衍、OffsetTime、Year番刊、YearMonth垄分、ZonedDateTime、HijrahDate计福、JapaneseDate跌捆、MinguoDate、ThaiBuddhistDate 驗(yàn)證日期為當(dāng)前時(shí)間之前 為null有效
@PastOrPresent Date象颖、Calendar佩厚、Instant、LocalDate说订、LocalDateTime抄瓦、LocalTime潮瓶、MonthDay、OffsetDateTime钙姊、OffsetTime毯辅、Year、YearMonth煞额、ZonedDateTime思恐、HijrahDate、JapaneseDate膊毁、MinguoDate胀莹、ThaiBuddhistDate 驗(yàn)證日期為當(dāng)前時(shí)間或之前 為null有效

數(shù)值檢查

注解 支持Java類型 說(shuō)明 備注
@Max BigDecimal、BigInteger婚温,byte嗜逻、short、int缭召、long以及包裝類 小于或等于 為null有效
@Min BigDecimal栈顷、BigInteger,byte嵌巷、short萄凤、int、long以及包裝類 大于或等于 為null有效
@DecimalMax BigDecimal搪哪、BigInteger靡努、CharSequence,byte晓折、short惑朦、int、long以及包裝類 小于或等于 為null有效
@DecimalMin BigDecimal漓概、BigInteger漾月、CharSequence,byte胃珍、short梁肿、int、long以及包裝類 大于或等于 為null有效
@Negative BigDecimal觅彰、BigInteger吩蔑,byte、short填抬、int烛芬、long、float、double以及包裝類 負(fù)數(shù) 為null有效赘娄,0無(wú)效
@NegativeOrZero BigDecimal仆潮、BigInteger,byte擅憔、short鸵闪、int檐晕、long暑诸、float、double以及包裝類 負(fù)數(shù)或零 為null有效
@Positive BigDecimal辟灰、BigInteger个榕,byte、short芥喇、int西采、long、float继控、double以及包裝類 正數(shù) 為null有效械馆,0無(wú)效
@PositiveOrZero BigDecimal、BigInteger武通,byte霹崎、short、int冶忱、long尾菇、float、double以及包裝類 正數(shù)或零 為null有效
@Digits(integer = 3, fraction = 2) BigDecimal囚枪、BigInteger派诬、CharSequence,byte链沼、short默赂、int、long以及包裝類 整數(shù)位數(shù)和小數(shù)位數(shù)上限 為null有效

其他

注解 支持Java類型 說(shuō)明 備注
@Pattern CharSequence 匹配指定的正則表達(dá)式 為null有效
@Email CharSequence 郵箱地址 為null有效括勺,默認(rèn)正則 '.*'
@Size CharSequence放可、Collection、Map朝刊、Array 大小范圍(length/size>0) 為null有效

hibernate-validator擴(kuò)展約束(部分)

注解 支持Java類型 說(shuō)明
@Length String 字符串長(zhǎng)度范圍
@Range 數(shù)值類型和String 指定范圍
@URL URL地址驗(yàn)證

自定義約束注解

除了以上提供的約束注解(大部分情況都是能夠滿足的)耀里,我們還可以根據(jù)自己的需求自定義自己的約束注解

定義自定義約束,有三個(gè)步驟

  • 創(chuàng)建約束注解
  • 實(shí)現(xiàn)一個(gè)驗(yàn)證器
  • 定義默認(rèn)的錯(cuò)誤信息

那么下面就直接來(lái)定義一個(gè)簡(jiǎn)單的驗(yàn)證手機(jī)號(hào)碼的注解

@Documented
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Constraint(validatedBy = {MobileValidator.class})
@Retention(RUNTIME)
@Repeatable(Mobile.List.class)
public @interface Mobile {

    /**
     * 錯(cuò)誤提示信息拾氓,可以寫死,也可以填寫國(guó)際化的key
     */
    String message() default "手機(jī)號(hào)碼不正確";

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

    String regexp() default "^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\\d{8}$";

    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Retention(RUNTIME)
    @Documented
    @interface List {
        Mobile[] value();
    }
}

關(guān)于注解的配置這里不說(shuō)了冯挎,自定義約束需要下面3個(gè)屬性

  • message 錯(cuò)誤提示信息,可以寫死,也可以填寫國(guó)際化的key
  • groups 分組信息,允許指定此約束所屬的驗(yàn)證組(下面會(huì)說(shuō)到分組約束)
  • payload 有效負(fù)載房官,可以通過payload來(lái)標(biāo)記一些需要特殊處理的操作

@Repeatable注解和List定義可以讓該注解在同一個(gè)位置重復(fù)多次趾徽,通常是不同的配置(比如不同的分組和消息)

@Constraint(validatedBy = {MobileValidator.class})該注解是指明我們的自定義約束的驗(yàn)證器,那下面就看一下驗(yàn)證器的寫法翰守,需要實(shí)現(xiàn)javax.validation.ConstraintValidator接口

public class MobileValidator implements ConstraintValidator<Mobile, String> {

    /**
     * 手機(jī)驗(yàn)證規(guī)則
     */
    private Pattern pattern;

    @Override
    public void initialize(Mobile mobile) {
        pattern = Pattern.compile(mobile.regexp());
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) {
            return true;
        }

        return pattern.matcher(value).matches();
    }
}

ConstraintValidator接口定義了在實(shí)現(xiàn)中設(shè)置的兩個(gè)類型參數(shù)孵奶。第一個(gè)指定要驗(yàn)證的注解類(如Mobile),第二個(gè)指定驗(yàn)證器可以處理的元素類型(如String)蜡峰;initialize()方法可以訪問約束注解的屬性值了袁;isValid()方法用于驗(yàn)證,返回true表示驗(yàn)證通過

Bean驗(yàn)證規(guī)范建議將空值視為有效湿颅。如果null不是元素的有效值载绿,則應(yīng)使用@NotNull 顯式注釋

到這里我們自定義的約束就寫好了,可以用個(gè)例子來(lái)測(cè)試一下

public class MobileTest {

    public void setMobile(@Mobile String mobile){
        // to do
    }

    private static ExecutableValidator executableValidator;

    @BeforeAll
    public static void setUpValidator() {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        executableValidator = factory.getValidator().forExecutables();
    }

    @Test
    public void manufacturerIsNull() throws NoSuchMethodException {
        MobileTest mobileTest = new MobileTest();

        Method method = MobileTest.class.getMethod("setMobile", String.class);
        Object[] parameterValues = {"1111111"};
        Set<ConstraintViolation<MobileTest>> violations = executableValidator.validateParameters(
                mobileTest, method, parameterValues);

        violations.forEach(violation -> System.out.println(violation.getMessage()));
    }
}
手機(jī)號(hào)碼不正確

分組約束

在上面的自定義約束中油航,有個(gè)groups屬性是用來(lái)指定驗(yàn)證約束的分組崭庸,我們?cè)跒閷傩约由献⒔獾臅r(shí)候,如果沒有配置分組信息谊囚,那么默認(rèn)會(huì)采用默認(rèn)分組 javax.validation.groups.Default

分組是用接口定義的怕享,用做標(biāo)識(shí),這里創(chuàng)建兩個(gè)標(biāo)識(shí)AddGroupUpdateGroup镰踏,分別標(biāo)識(shí)新增和修改

public interface AddGroup {
}

public interface UpdateGroup {
}

然后對(duì)我們的User對(duì)象的id屬性做分組標(biāo)識(shí)

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {

    @Null(groups = AddGroup.class)
    @NotBlank(groups = UpdateGroup.class)
    private String id;
    
    // ... 省略了其他屬性
}    

我們看下如何使用

@Test
public void validatorGroupTest() {
    User user = new User();

    // 檢查給定類的單個(gè)屬性是否可以成功驗(yàn)證
    Set<ConstraintViolation<User>> constraintViolations = validator.validateValue(User.class, "id", "", UpdateGroup.class);
    Set<ConstraintViolation<User>> constraintViolations2 = validator.validateValue(User.class, "id", "");
    Set<ConstraintViolation<User>> constraintViolations3 = validator.validateValue(User.class, "id", "", AddGroup.class);

    constraintViolations.forEach(constraintViolation -> System.out.println(constraintViolation.getMessage()));
    constraintViolations2.forEach(constraintViolation -> System.out.println(constraintViolation.getMessage()));
    constraintViolations3.forEach(constraintViolation -> System.out.println(constraintViolation.getMessage()));
}

上面的測(cè)試只有加了UpdateGroug分組才會(huì)驗(yàn)證函筋,返回錯(cuò)誤信息,而下面的constraintViolations2并不會(huì)去驗(yàn)證余境,因?yàn)槟J(rèn)會(huì)采用Default分組驻呐。如果想要不標(biāo)記分組的時(shí)候,也會(huì)去驗(yàn)證Default分組芳来,可以去繼承默認(rèn)分組

public interface AddGroup extends Default {
}

在Spring中使用Hibernate Validator

上面介紹了Validator的一些使用含末,還有注解的介紹,那么在Spring中我們?cè)趺慈ナ褂肏ibernate Validator做驗(yàn)證呢即舌?或者說(shuō)再Web項(xiàng)目中怎么使用Hibernate Validator佣盒?

spring-boot-starter-web中是添加了hibernate-validator依賴的,說(shuō)明Spring Boot本身也是使用到了Hibernate Validator驗(yàn)證框架的

配置Validator

@Configuration
public class ValidatorConfig {

    /**
     * 配置驗(yàn)證器
     *
     * @return validator
     */
    @Bean
    public Validator validator() {
        ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
                .configure()
                // 快速失敗模式
                .failFast(true)
                // .addProperty( "hibernate.validator.fail_fast", "true" )
                .buildValidatorFactory();
        return validatorFactory.getValidator();
    }
}

可以通過方法 failFast(true)addProperty("hibernate.validator.fail_fast", "true")設(shè)置為快速失敗模式顽聂,快速失敗模式在校驗(yàn)過程中肥惭,當(dāng)遇到第一個(gè)不滿足條件的參數(shù)時(shí)就立即返回,不再繼續(xù)后面參數(shù)的校驗(yàn)紊搪。否則會(huì)一次性校驗(yàn)所有參數(shù)蜜葱,并返回所有不符合要求的錯(cuò)誤信息

如果是Spring MVC的話,需要xml配置可參考下面的配置

<mvc:annotation-driven validator="validator"/>

<!-- validator基本配置 -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
    <!-- 映射資源文件 -->
    <property name="validationMessageSource" ref="messageSource"/>
</bean>

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource" name="messageSource">
    <!--<property name="basenames">
                <list>
                    <value>classpath:messages/messages</value>
                    <value>classpath:messages/ValidationMessages</value>
                </list>
            </property>-->
    <property name="useCodeAsDefaultMessage" value="false" />
    <property name="defaultEncoding" value="UTF-8" />
    <property name="cacheSeconds" value="60" />
</bean>

請(qǐng)求參數(shù)bean驗(yàn)證

接口上的Bean驗(yàn)證耀石,需要在參數(shù)前加上@Valid或Spring的 @Validated注解牵囤,這兩種注釋都會(huì)導(dǎo)致應(yīng)用標(biāo)準(zhǔn)Bean驗(yàn)證。如果驗(yàn)證不通過會(huì)拋出BindException異常,并變成400(BAD_REQUEST)響應(yīng)揭鳞;或者可以通過ErrorsBindingResult參數(shù)在控制器內(nèi)本地處理驗(yàn)證錯(cuò)誤炕贵。另外,如果參數(shù)前有@RequestBody注解野崇,驗(yàn)證錯(cuò)誤會(huì)拋出MethodArgumentNotValidException異常称开。

@RestController
public class UserController {

    @PostMapping("/user")
    public R handle(@Valid @RequestBody User user, BindingResult result) {
        // 在控制器內(nèi)本地處理驗(yàn)證錯(cuò)誤
        if (result.hasErrors()) {
            result.getAllErrors().forEach(s -> System.out.println(s.getDefaultMessage()));
             return R.fail(result.getAllErrors().get(0).getDefaultMessage());
        }
        // ...
        return R.success();
    }

    @PostMapping("/user2")
    public R handle2(@Valid User user, BindingResult result) {
        // 在控制器內(nèi)本地處理驗(yàn)證錯(cuò)誤
        if (result.hasErrors()) {
            result.getAllErrors().forEach(s -> System.out.println(s.getDefaultMessage()));
             return R.fail(result.getAllErrors().get(0).getDefaultMessage());
        }
        // ...
        return R.success();
    }

    /**
     * 驗(yàn)證不通過拋出 `MethodArgumentNotValidException`
     */
    @PostMapping("/user3")
    public R handle3(@Valid @RequestBody User user) {
        // ...
        return R.success();
    }

    /**
     * 驗(yàn)證不通過拋出 `BindException`
     */
    @PostMapping("/user4")
    public R handle4(@Valid User user) {
        // ...
        return R.success();
    }
}

配合Spring的BindingResult參數(shù),我們是可以在控制器中去處理驗(yàn)證錯(cuò)誤乓梨,不過通常也是把驗(yàn)證錯(cuò)誤的消息轉(zhuǎn)成我們自己的返回格式鳖轰,那么在每個(gè)方法中都去做這樣的驗(yàn)證錯(cuò)誤處理,顯然是沒有必要的督禽。我們可以利用驗(yàn)證不通過的異常來(lái)做統(tǒng)一的錯(cuò)誤處理

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * hibernate validator 數(shù)據(jù)綁定驗(yàn)證異常攔截
     *
     * @param e 綁定驗(yàn)證異常
     * @return 錯(cuò)誤返回消息
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(BindException.class)
    public R validateErrorHandler(BindException e) {
        ObjectError error = e.getAllErrors().get(0);
        log.info("數(shù)據(jù)驗(yàn)證異常:{}", error.getDefaultMessage());
        return R.fail(error.getDefaultMessage());
    }

    /**
     * hibernate validator 數(shù)據(jù)綁定驗(yàn)證異常攔截
     *
     * @param e 綁定驗(yàn)證異常
     * @return 錯(cuò)誤返回消息
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R validateErrorHandler(MethodArgumentNotValidException e) {
        ObjectError error = e.getBindingResult().getAllErrors().get(0);
        log.info("數(shù)據(jù)驗(yàn)證異常:{}", error.getDefaultMessage());
        return R.fail(error.getDefaultMessage());
    }
}    

方法參數(shù)驗(yàn)證

配置

Hibernate Validator是可以在方法級(jí)驗(yàn)證參數(shù)的脆霎,Spring中當(dāng)然也是有實(shí)現(xiàn)的总处。

我們?cè)赩alidator的配置中狈惫,添加MethodValidationPostProcessorBean,在上面的ValidatorConfig.java中添加一下配置

/**
 * 設(shè)置方法參數(shù)驗(yàn)證器
 */
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
    MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
    // 設(shè)置validator模式為快速失敗返回
    postProcessor.setValidator(validator());
    return postProcessor;
}

如果是Spring Mvc鹦马,那么要在spring-mvc.xml中聲明bean信息胧谈,不然在Controller里面是無(wú)效的

<!-- 設(shè)置方法參數(shù)驗(yàn)證器 -->
<bean id="methodValidationPostProcessor" class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
    <property name="validator" ref="validator"/>
</bean>

使用

配置了上面的MethodValidationPostProcessor,我們就可以在方法參數(shù)或返回值使用約束注解了荸频,要注意的是菱肖,在要使用參數(shù)驗(yàn)證的類上一定要加上@Validated注解,否則無(wú)效

/**
 * 一定要加上 `@Validated` 注解
 */
@Validated
@RestController
public class UserController {

    @GetMapping("/user")
    public R handle(@Mobile String mobile) {
        // ...
        return R.success();
    }
}

如果驗(yàn)證不通過旭从,會(huì)拋出ConstraintViolationException異常稳强,同樣的,我們可以在全局的異常處理器里面處理驗(yàn)證錯(cuò)誤和悦,在GlobalExceptionHandler中添加一下代碼

/**
 * spring validator 方法參數(shù)驗(yàn)證異常攔截
 *
 * @param e 綁定驗(yàn)證異常
 * @return 錯(cuò)誤返回消息
 */
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(ConstraintViolationException.class)
public R defaultErrorHandler(ConstraintViolationException e) {
    Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
    ConstraintViolation<?> violation = violations.iterator().next();
    log.info("數(shù)據(jù)驗(yàn)證異常:{}", violation.getMessage());
    return R.fail(violation.getMessage());
}

分組

Spring的@Validate注解是可以支持分組驗(yàn)證的

@PostMapping("/user")
public R handle(@Validated(AddGroup.class) @RequestBody User user) {
    // ...
    return R.success();
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末退疫,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子鸽素,更是在濱河造成了極大的恐慌褒繁,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件馍忽,死亡現(xiàn)場(chǎng)離奇詭異棒坏,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)遭笋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門坝冕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人瓦呼,你說(shuō)我怎么就攤上這事喂窟。” “怎么了?”我有些...
    開封第一講書人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵谎替,是天一觀的道長(zhǎng)偷溺。 經(jīng)常有香客問我,道長(zhǎng)钱贯,這世上最難降的妖魔是什么挫掏? 我笑而不...
    開封第一講書人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮秩命,結(jié)果婚禮上尉共,老公的妹妹穿的比我還像新娘。我一直安慰自己弃锐,他們只是感情好袄友,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著霹菊,像睡著了一般剧蚣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上旋廷,一...
    開封第一講書人閱讀 52,736評(píng)論 1 312
  • 那天鸠按,我揣著相機(jī)與錄音,去河邊找鬼饶碘。 笑死目尖,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的扎运。 我是一名探鬼主播瑟曲,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼豪治!你這毒婦竟也來(lái)了洞拨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤鬼吵,失蹤者是張志新(化名)和其女友劉穎扣甲,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體齿椅,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡琉挖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了涣脚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片示辈。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖遣蚀,靈堂內(nèi)的尸體忽然破棺而出矾麻,到底是詐尸還是另有隱情纱耻,我是刑警寧澤,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布险耀,位于F島的核電站弄喘,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏甩牺。R本人自食惡果不足惜蘑志,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贬派。 院中可真熱鬧急但,春花似錦、人聲如沸搞乏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)请敦。三九已至镐躲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間冬三,已是汗流浹背匀油。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工缘缚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留勾笆,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓桥滨,卻偏偏與公主長(zhǎng)得像窝爪,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子齐媒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361