Java Bean Validation 最佳實踐


Java Bean Validation 最佳實踐

參數校驗是我們程序開發中必不可少的過程。用戶在前端頁面上填寫表單時,前端js程序會校驗參數的合法性,當數據到了后端,為了防止惡意操作,保持程序的健壯性,后端同樣需要對數據進行校驗。后端參數校驗最簡單的做法是直接在業務方法里面進行判斷,當判斷成功之后再繼續往下執行。但這樣帶給我們的是代碼的耦合,冗余。當我們多個地方需要校驗時,我們就需要在每一個地方調用校驗程序,導致代碼很冗余,且不美觀。

那么如何優雅的對參數進行校驗呢?JSR303就是為了解決這個問題出現的,本篇文章主要是介紹 JSR303,Hibernate Validator 等校驗工具的使用,以及自定義校驗注解的使用。

校驗框架介紹

JSR303 是一套JavaBean參數校驗的標准,它定義了很多常用的校驗注解,我們可以直接將這些注解加在我們JavaBean的屬性上面,就可以在需要校驗的時候進行校驗了。注解如下:

Hibernate validator 在JSR303的基礎上對校驗注解進行了擴展,擴展注解如下:

Spring validtor 同樣擴展了jsr303,並實現了方法參數和返回值的校驗

Spring 提供了MethodValidationPostProcessor類,用於對方法的校驗

@NotEmpty,@NotNull和@NotBlank的區別

1 @NotEmpty :不能為null,且Size>0

2 @NotNull:不能為null,但可以為empty,沒有Size的約束

3 @NotBlank:只用於String,不能為null且trim()之后size>0

代碼實現

添加JAR包依賴

在pom.xml中添加如下依賴:

復制代碼
        <!--jsr 303-->
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>1.1.0.Final</version>
        </dependency>
        <!-- hibernate validator-->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.2.0.Final</version>
        </dependency>
復制代碼

 

最簡單的參數校驗

1、Model 中添加校驗注解

復制代碼
public class Book {
    private long id;
</span><span style="color: #008000">/**</span><span style="color: #008000">
 * 書名
 </span><span style="color: #008000">*/</span><span style="color: #000000">
@NotEmpty(message </span>= "書名不能為空"<span style="color: #000000">)
</span><span style="color: #0000ff">private</span><span style="color: #000000"> String bookName;
</span><span style="color: #008000">/**</span><span style="color: #008000">
 * ISBN號
 </span><span style="color: #008000">*/</span><span style="color: #000000">
@NotNull(message </span>= "ISBN號不能為空"<span style="color: #000000">)
</span><span style="color: #0000ff">private</span><span style="color: #000000"> String bookIsbn;
</span><span style="color: #008000">/**</span><span style="color: #008000">
 * 單價
 </span><span style="color: #008000">*/</span><span style="color: #000000">
@DecimalMin(value </span>= "0.1",message = "單價最低為0.1"<span>)<br>    </span><span style="color: #0000ff">private</span> <span style="color: #0000ff">double</span><span> price; </span><span style="color: #008000">//</span><span style="color: #008000"> getter setter .......</span> <span> }</span></pre>
復制代碼

 

2、在controller中使用此校驗

復制代碼
 /**
     * 添加Book對象
     * @param book
     */
    @RequestMapping(value = "/book", method = RequestMethod.POST)
    public void addBook(@RequestBody @Valid Book book) {
        System.out.println(book.toString());
    }
復制代碼

 

當訪問這個post接口時,如果參數不符合Model中定義的話,程序中就回拋出400異常,並提示錯誤信息。

自定義校驗注解

雖然jSR303和Hibernate Validtor 已經提供了很多校驗注解,但是當面對復雜參數校驗時,還是不能滿足我們的要求,這時候我們就需要 自定義校驗注解。

下面以“List數組中不能含有null元素”為實例自定義校驗注解

1、注解定義如下:

復制代碼
package com.beiyan.validate.annotation;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**

  • 自定義參數校驗注解
  • 校驗 List 集合中是否有null 元素
    */

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = ListNotHasNullValidatorImpl.class)////此處指定了注解的實現類為ListNotHasNullValidatorImpl

public @interface ListNotHasNull {

</span><span style="color: #008000">/**</span><span style="color: #008000">
 * 添加value屬性,可以作為校驗時的條件,若不需要,可去掉此處定義
 </span><span style="color: #008000">*/</span>
<span style="color: #0000ff">int</span> value() <span style="color: #0000ff">default</span> 0<span style="color: #000000">;

String message() </span><span style="color: #0000ff">default</span> "List集合中不能含有null元素"<span style="color: #000000">;

Class</span>&lt;?&gt;[] groups() <span style="color: #0000ff">default</span><span style="color: #000000"> {};

Class</span>&lt;? <span style="color: #0000ff">extends</span> Payload&gt;[] payload() <span style="color: #0000ff">default</span><span style="color: #000000"> {};

</span><span style="color: #008000">/**</span><span style="color: #008000">
 * 定義List,為了讓Bean的一個屬性上可以添加多套規則
 </span><span style="color: #008000">*/</span><span style="color: #000000">
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@</span><span style="color: #0000ff">interface</span><span style="color: #000000"> List {
    ListNotHasNull[] value();
}

}

復制代碼

 

2、注解實現類: 

復制代碼
package com.beiyan.validate.annotation;

import org.springframework.stereotype.Service;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.List;

/**

  • 自定義注解ListNotHasNull 的實現類
  • 用於判斷List集合中是否含有null元素
    */

@Service
public class ListNotHasNullValidatorImpl implements ConstraintValidator<ListNotHasNull, List> {

</span><span style="color: #0000ff">private</span> <span style="color: #0000ff">int</span><span style="color: #000000"> value;

@Override
</span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> initialize(ListNotHasNull constraintAnnotation) {
    </span><span style="color: #008000">//</span><span style="color: #008000">傳入value 值,可以在校驗中使用</span>
    <span style="color: #0000ff">this</span>.value =<span style="color: #000000"> constraintAnnotation.value();
}

</span><span style="color: #0000ff">public</span> <span style="color: #0000ff">boolean</span><span style="color: #000000"> isValid(List list, ConstraintValidatorContext constraintValidatorContext) {
    </span><span style="color: #0000ff">for</span><span style="color: #000000"> (Object object : list) {
        </span><span style="color: #0000ff">if</span> (object == <span style="color: #0000ff">null</span><span style="color: #000000">) {
            </span><span style="color: #008000">//</span><span style="color: #008000">如果List集合中含有Null元素,校驗失敗</span>
            <span style="color: #0000ff">return</span> <span style="color: #0000ff">false</span><span style="color: #000000">;
        }
    }
    </span><span style="color: #0000ff">return</span> <span style="color: #0000ff">true</span><span style="color: #000000">;
}

}

復制代碼

 

3、model添加注解:

復制代碼
public class User {

//其他參數 .......

/**
* 所擁有的書籍列表
*/
@NotEmpty(message
= "所擁有書籍不能為空")
@ListNotHasNull(message
= "List 中不能含有null元素")
@Valid
private List<Book> books;
//getter setter 方法.......
}

復制代碼

 

使用方法同上,在在需要校驗的Model上面加上@Valid 即可

分組驗證

對同一個Model,我們在增加和修改時對參數的校驗也是不一樣的,這個時候我們就需要定義分組驗證,步驟如下

1、定義兩個空接口,分別代表Person對象的增加校驗規則和修改校驗規則

復制代碼
/**
 * 可以在一個Model上面添加多套參數驗證規則,此接口定義添加Person模型新增時的參數校驗規則
 */
public interface PersonAddView {
}

/**

  • 可以在一個Model上面添加多套參數驗證規則,此接口定義添加Person模型修改時的參數校驗規則
    */
    public interface PersonModifyView {
    }
復制代碼

 

2、Model上添加注解時使用指明所述的分組

復制代碼
public class Person {
    private long id;
    /**
     * 添加groups 屬性,說明只在特定的驗證規則里面起作用,不加則表示在使用Deafault規則時起作用
     */
    @NotNull(groups = {PersonAddView.class, PersonModifyView.class}, message = "添加、修改用戶時名字不能為空", payload = ValidateErrorLevel.Info.class)
    @ListNotHasNull.List({
            @ListNotHasNull(groups = {PersonAddView.class}, message = "添加上Name不能為空"),
            @ListNotHasNull(groups = {PersonModifyView.class}, message = "修改時Name不能為空")})
    private String name;
@NotNull(groups </span>= {PersonAddView.<span style="color: #0000ff">class</span>}, message = "添加用戶時地址不能為空"<span style="color: #000000">)
</span><span style="color: #0000ff">private</span><span style="color: #000000"> String address;

@Min(value </span>= 18, groups = {PersonAddView.<span style="color: #0000ff">class</span>}, message = "姓名不能低於18歲"<span style="color: #000000">)
@Max(value </span>= 30, groups = {PersonModifyView.<span style="color: #0000ff">class</span>}, message = "姓名不能超過30歲"<span style="color: #000000">)
</span><span style="color: #0000ff">private</span> <span style="color: #0000ff">int</span><span style="color: #000000"> age;

//getter setter 方法......
}

復制代碼

 

3、啟用校驗

此時啟用校驗和之前的不同,需要指明啟用哪一組規則

復制代碼
  /**
     * 添加一個Person對象
     * 此處啟用PersonAddView 這個驗證規則
     * 備注:此處@Validated(PersonAddView.class) 表示使用PersonAndView這套校驗規則,若使用@Valid 則表示使用默認校驗規則,
     * 若兩個規則同時加上去,則只有第一套起作用
     */
    @RequestMapping(value = "/person", method = RequestMethod.POST)
    public void addPerson(@RequestBody @Validated({PersonAddView.class, Default.class}) Person person) {
        System.out.println(person.toString());
    }
</span><span style="color: #008000">/**</span><span style="color: #008000">
 * 修改Person對象
 * 此處啟用PersonModifyView 這個驗證規則
 </span><span style="color: #008000">*/</span><span style="color: #000000">
@RequestMapping(value </span>= "/person", method =<span style="color: #000000"> RequestMethod.PUT)
</span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> modifyPerson(@RequestBody @Validated(value = {PersonModifyView.<span style="color: #0000ff">class</span><span style="color: #000000">}) Person person) {
    System.out.println(person.toString());
}</span></pre>
復制代碼

 

Spring validator 方法級別的校驗

JSR和Hibernate validator的校驗只能對Object的屬性進行校驗,不能對單個的參數進行校驗,spring 在此基礎上進行了擴展,添加了MethodValidationPostProcessor攔截器,可以實現對方法參數的校驗,實現如下:

1、實例化MethodValidationPostProcessor

@Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        return new MethodValidationPostProcessor();
    }

 

2、在所要實現方法參數校驗的類上面添加@Validated,如下

@RestController
@Validated
public class ValidateController {
}

 

3、在方法上面添加校驗規則:

  @RequestMapping(value = "/test", method = RequestMethod.GET)
    public String paramCheck(@Length(min = 10) @RequestParam String name) {
        System.out.println(name);
        return null;
    }

 

當方法上面的參數校驗失敗,spring 框架就回拋出異常

復制代碼
{
  "timestamp": 1476108200558,
  "status": 500,
  "error": "Internal Server Error",
  "exception": "javax.validation.ConstraintViolationException",
  "message": "No message available",
  "path": "/test"
}
復制代碼

從此可以優雅的對參數進行校驗了 

寫在后面的話:

本篇文章只列舉了常用的幾種校驗方法,其實關於校驗的內容還有很多:

校驗信息的國際化顯示,

組合參數校驗,

message中使用EL表達式,

將校驗信息綁定到ModelAndView等,這里就不一一列出了,下面這幾篇文章寫的也不錯,讀者可以參考:

將校驗信息綁定到ModelAndView    http://www.voidcn.com/blog/983836259/article/p-5794496.html

集成Bean Validation 1.1(JSR-349)到SpringMVC   https://my.oschina.net/qjx1208/blog/200946

 

本文的全部代碼已上傳開源中國git倉庫: http://git.oschina.net/beiyan/Validate

 

posted @ 2018-12-08 17:28  學不死的程序員  閱讀( 1098)  評論( 0編輯  收藏


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM