SpringBoot校驗(yàn)(validation)

title: SpringBoot校驗(yàn)(validation)
date: 2019-07-15
author: maxzhao
tags:
  - SpringBoot
  - validation
  - hibernate-validation
categories:
  - SpringBoot

元編程

一個(gè)健壯的系統(tǒng)都要對(duì)外部提交的數(shù)據(jù)進(jìn)行完整性、合法性的校驗(yàn)。

校驗(yàn)是我們程序開發(fā)中必不可少的過程。

即使開發(fā)一個(gè)不面對(duì)最終用戶的工具包,也需要對(duì)傳入的數(shù)據(jù)進(jìn)行縝密的校驗(yàn)來防止引發(fā)底層難以追蹤的問題。

后端參數(shù)校驗(yàn)最簡(jiǎn)單的做法是直接在業(yè)務(wù)方法里面進(jìn)行判斷,當(dāng)判斷成功之后再繼續(xù)往下執(zhí)行。但這樣帶給我們的是代碼的耦合,冗余。當(dāng)我們多個(gè)地方需要校驗(yàn)時(shí),我們就需要在每一個(gè)地方調(diào)用校驗(yàn)程序,導(dǎo)致代碼很冗余,且不美觀。

不使用Bean Validation校驗(yàn)數(shù)據(jù)的代碼基本都是靠大量的 if-else.所以我這里學(xué)習(xí)使用了 注解方式實(shí)現(xiàn)數(shù)據(jù)校驗(yàn).

使用

SpringBoot 中的 bean validation 是集成了hibernate-validatortomcat-embed-el

引入依賴

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

簡(jiǎn)單的校驗(yàn)

@Valid:常見用在方法,類中字段上進(jìn)行校驗(yàn)

@Validated:是spring提供的對(duì)@Valid的封裝,常見用在方法上進(jìn)行校驗(yàn)

BindingResult:是驗(yàn)證是否錯(cuò)誤

Model中

@Range(max = 150, min = 1, message = "年齡范圍應(yīng)該在1-150內(nèi)。")
private Integer age;

Controller中

@PostMapping("save")
public void v1(@RequestBody @Valid AppUser appUser,BindingResult result){
      if(result.hasErrors()){
            for (ObjectError error : result.getAllErrors()) {
                System.out.println(error.getDefaultMessage());
            }
        }
}

綁定多個(gè)校驗(yàn)對(duì)象

@PostMapping("save")
public void v1(@RequestBody @Valid AppUser appUser,BindingResult result,@RequestBody @Valid AppUser appUser2,BindingResult result2){
      if(result.hasErrors()){
            for (ObjectError error : result.getAllErrors()) {
                System.out.println(error.getDefaultMessage());
            }
        }
}

部分標(biāo)簽

Bean Validation 中內(nèi)置的 constraint

注解 作用
@Valid 被注釋的元素是一個(gè)對(duì)象,需要檢查此對(duì)象的所有字段值
@Null 被注釋的元素必須為 null
@NotNull 被注釋的元素必須不為 null
@AssertTrue 被注釋的元素必須為 true
@AssertFalse 被注釋的元素必須為 false
@Min(value) 被注釋的元素必須是一個(gè)數(shù)字,其值必須大于等于指定的最小值
@Max(value) 被注釋的元素必須是一個(gè)數(shù)字,其值必須小于等于指定的最大值
@DecimalMin(value) 被注釋的元素必須是一個(gè)數(shù)字,其值必須大于等于指定的最小值
@DecimalMax(value) 被注釋的元素必須是一個(gè)數(shù)字,其值必須小于等于指定的最大值
@Size(max, min) 被注釋的元素的大小必須在指定的范圍內(nèi)
@Digits (integer, fraction) 被注釋的元素必須是一個(gè)數(shù)字,其值必須在可接受的范圍內(nèi)
@Past 被注釋的元素必須是一個(gè)過去的日期
@Future 被注釋的元素必須是一個(gè)將來的日期
@Pattern(value) 被注釋的元素必須符合指定的正則表達(dá)式

Hibernate Validator 附加的 constraint

注解 作用
@Email 被注釋的元素必須是電子郵箱地址
@Length(min=, max=) 被注釋的字符串的大小必須在指定的范圍內(nèi)
@NotEmpty 被注釋的字符串的必須非空
@Range(min=, max=) 被注釋的元素必須在合適的范圍內(nèi)
@NotBlank 被注釋的字符串的必須非空
@URL(protocol=,host=, port=,regexp=, flags=) 被注釋的字符串必須是一個(gè)有效的url

官方詳細(xì)

中文詳細(xì)

容易記錯(cuò)的

@NotNull 任何對(duì)象的value不能為null

@NotEmpty 集合對(duì)象的元素不為0,即集合不為空,也可以用于字符串不為null

@NotBlank 只能用于字符串不為null,并且字符串trim()以后length要大于0

resource 下新建錯(cuò)誤信息配置文件

在resource 目錄下新建提示信息配置文件 ValidationMessages.properties

文件中的格式為message=ASCII.

中文的ascii碼

hibernate的校驗(yàn)?zāi)J?/h2>

1、普通模式(默認(rèn)是這個(gè)模式)

普通模式(會(huì)校驗(yàn)完所有的屬性,然后返回所有的驗(yàn)證失敗信息)

2、快速失敗返回模式

快速失敗返回模式(只要有一個(gè)驗(yàn)證失敗,則返回)

兩種驗(yàn)證模式配置方式

參考官方文檔

failFast:true 快速失敗返回模式 false 普通模式

@Configuration
public class ValidatorConfiguration {
    @Bean
    public Validator validator(){
        ValidatorFactory validatorFactory = Validation
        .byProvider( HibernateValidator.class )        
            .configure()        
            .failFast( true )        
            .buildValidatorFactory();
        Validator validator = validatorFactory.getValidator();
    }
}

和 (hibernate.validator.fail_fast:true 快速失敗返回模式 false 普通模式)

@Configuration
public class ValidatorConfiguration {
       @Bean
       public Validator validator() {
           ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
                   .configure()
                   .addProperty("hibernate.validator.fail_fast", "true")
                   .buildValidatorFactory();
           Validator validator = validatorFactory.getValidator();
        return validator;
       }
   }

手動(dòng)驗(yàn)證Model

AppUser appUser=new AppUser();
Set<ConstraintViolation<AppUser>> violationSet = validator.validate(appUser);
   for (ConstraintViolation<AppUser> model : violationSet) {
        System.out.println(model.getMessage());
   }

分組校驗(yàn)(區(qū)分新增和修改的規(guī)則)

簡(jiǎn)單使用

場(chǎng)景

新增用戶信息和修改用戶信息所需要驗(yàn)證的字段是不同的.

public interface AppUserVaildC {
}

public interface AppUserVaildU {
}

Model中

Default 是默認(rèn)分組.

@Range(min = 0,max = 100,message = "年齡必須在[0,100]",groups={Default.class})
    /**年齡*/
private Integer age;
@Range(min = 0,max = 2,message = "性別必須在[0,2]",groups = {AppUserVaildC.class})
    /**性別 0:未知;1:男;2:女*/
private Integer sex;

Controller中使用

@PostMapping("save")
public void v1(@RequestBody @Validated({AppUserVaildC.class, AppUserVaildU.class}) AppUser appUser,BindingResult result){
      if(result.hasErrors()){
            for (ObjectError error : result.getAllErrors()) {
                System.out.println(error.getDefaultMessage());
            }
        }
}

普通使用

AppUser appUser=new AppUser();
Set<ConstraintViolation<AppUser>> violationSet = validator.validate(appUser,AppUserVaildC,AppUserVaildU);
   for (ConstraintViolation<AppUser> model : violationSet) {
        System.out.println(model.getMessage());
   }

組序列

除了按組指定是否驗(yàn)證之外,還可以指定組的驗(yàn)證順序,前面組驗(yàn)證不通過的,后面組不進(jìn)行驗(yàn)證

@GroupSequence({AppUserVaildC.class, AppUserVaildU.class, Default.class})
public interface GroupOrder {
}

Controller中使用

@PostMapping("save")
public void v1(@RequestBody @Validated({GroupOrder.class}) AppUser appUser,BindingResult result){
      if(result.hasErrors()){
            for (ObjectError error : result.getAllErrors()) {
                System.out.println(error.getDefaultMessage());
            }
        }
}

普通使用

AppUser appUser=new AppUser();
Set<ConstraintViolation<AppUser>> violationSet = validator.validate(appUser,GroupOrder);
   for (ConstraintViolation<AppUser> model : violationSet) {
        System.out.println(model.getMessage());
   }

自定義驗(yàn)證

下面是一個(gè)自定義大小寫的驗(yàn)證

public enum CaseMode {
    UPPER,
    LOWER;
}


@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CheckCaseValidator.class)
@Documented
public @interface CheckCase {
    String message() default "";

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

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

    CaseMode value();
}


public class CheckCaseValidator implements ConstraintValidator<CheckCase, String> {
    private CaseMode caseMode;
    public void initialize(CheckCase checkCase) {
        this.caseMode = checkCase.value();
    }

    public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
        if (s == null) {
            return true;
        }

        if (caseMode == CaseMode.UPPER) {
            return s.equals(s.toUpperCase());
        } else {
            return s.equals(s.toLowerCase());
        }
    }
}

Model中

@Range(value = CaseMode.LOWER ,message = "年必須是小寫",groups={Default.class})
    /**年齡*/
private String loginName;
@Range(min = 0,max = 2,message = "性別必須在[0,2]",groups = {AppUserVaildC.class})
    /**性別 0:未知;1:男;2:女*/
private Integer sex;

validator 配置

    @Bean
    public Validator validator(){
        ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
                .configure()
                .addProperty( "hibernate.validator.fail_fast", "true" )
                .buildValidatorFactory();
        Validator validator = validatorFactory.getValidator();

        return validator;
    }

本文地址:

SpringBoot校驗(yàn)(validation)

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

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