SpringBoot校驗(validation)

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

元編程

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

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

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

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

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

使用

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

引入依賴

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

簡單的校驗

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

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

BindingResult:是驗證是否錯誤

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());
            }
        }
}

綁定多個校驗對象

@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 被注釋的元素是一個對象,需要檢查此對象的所有字段值
@Null 被注釋的元素必須為 null
@NotNull 被注釋的元素必須不為 null
@AssertTrue 被注釋的元素必須為 true
@AssertFalse 被注釋的元素必須為 false
@Min(value) 被注釋的元素必須是一個數(shù)字婆翔,其值必須大于等于指定的最小值
@Max(value) 被注釋的元素必須是一個數(shù)字拯杠,其值必須小于等于指定的最大值
@DecimalMin(value) 被注釋的元素必須是一個數(shù)字,其值必須大于等于指定的最小值
@DecimalMax(value) 被注釋的元素必須是一個數(shù)字啃奴,其值必須小于等于指定的最大值
@Size(max, min) 被注釋的元素的大小必須在指定的范圍內(nèi)
@Digits (integer, fraction) 被注釋的元素必須是一個數(shù)字潭陪,其值必須在可接受的范圍內(nèi)
@Past 被注釋的元素必須是一個過去的日期
@Future 被注釋的元素必須是一個將來的日期
@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=) 被注釋的字符串必須是一個有效的url

官方詳細(xì)

中文詳細(xì)

容易記錯的

@NotNull 任何對象的value不能為null

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

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

resource 下新建錯誤信息配置文件

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

文件中的格式為message=ASCII.

中文的ascii碼

hibernate的校驗?zāi)J?/h2>

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

普通模式(會校驗完所有的屬性瘟则,然后返回所有的驗證失敗信息)

2黎炉、快速失敗返回模式

快速失敗返回模式(只要有一個驗證失敗,則返回)

兩種驗證模式配置方式

參考官方文檔

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;
       }
   }

手動驗證Model

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

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

簡單使用

場景

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

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());
   }

組序列

除了按組指定是否驗證之外,還可以指定組的驗證順序丹壕,前面組驗證不通過的庆械,后面組不進(jì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());
   }

自定義驗證

下面是一個自定義大小寫的驗證

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校驗(validation)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末缭乘,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子琉用,更是在濱河造成了極大的恐慌堕绩,老刑警劉巖策幼,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異奴紧,居然都是意外死亡特姐,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門绰寞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來到逊,“玉大人,你說我怎么就攤上這事滤钱【鹾” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵件缸,是天一觀的道長铜靶。 經(jīng)常有香客問我,道長他炊,這世上最難降的妖魔是什么争剿? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮痊末,結(jié)果婚禮上蚕苇,老公的妹妹穿的比我還像新娘。我一直安慰自己凿叠,他們只是感情好涩笤,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著盒件,像睡著了一般蹬碧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上炒刁,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天恩沽,我揣著相機與錄音,去河邊找鬼翔始。 笑死罗心,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的城瞎。 我是一名探鬼主播渤闷,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼全谤!你這毒婦竟也來了肤晓?” 一聲冷哼從身側(cè)響起爷贫,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤认然,失蹤者是張志新(化名)和其女友劉穎补憾,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體卷员,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡盈匾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了毕骡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片削饵。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖未巫,靈堂內(nèi)的尸體忽然破棺而出窿撬,到底是詐尸還是另有隱情,我是刑警寧澤叙凡,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布劈伴,位于F島的核電站,受9級特大地震影響握爷,放射性物質(zhì)發(fā)生泄漏扫皱。R本人自食惡果不足惜史简,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧加缘,春花似錦、人聲如沸宴合。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叨吮。三九已至辆布,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間茶鉴,已是汗流浹背锋玲。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留涵叮,地道東北人惭蹂。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像割粮,于是被迫代替她去往敵國和親盾碗。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355