37. Validation
The method validation feature supported by Bean Validation 1.1 is automatically enabled as long as a JSR-303 implementation (such as Hibernate validator) is on the classpath. This lets bean methods be annotated with javax.validation constraints on their parameters and/or on their return value. Target classes with such annotated methods need to be annotated with the @Validated annotation at the type level for their methods to be searched for inline constraint annotations.
For instance, the following service triggers the validation of the first argument, making sure its size is between 8 and 10:
@Service @Validated public class MyBean { public Archive findByCodeAndAuthor(@Size(min = 8, max = 10) String code, Author author) { ... } }
https://docs.spring.io/spring-boot/docs/2.1.10.RELEASE/reference/html/boot-features-validation.html
參數校驗是我們程序開發中必不可少的過程。用戶在前端頁面上填寫表單時,前端js程序會校驗參數的合法性,當數據到了后端,為了防止惡意操作,保持程序的健壯性,后端同樣需要對數據進行校驗。后端參數校驗最簡單的做法是直接在業務方法里面進行判斷,當判斷成功之后再繼續往下執行。但這樣帶給我們的是代碼的耦合,冗余。當我們多個地方需要校驗時,我們就需要在每一個地方調用校驗程序,導致代碼很冗余,且不美觀。
那么如何優雅的對參數進行校驗呢?JSR303就是為了解決這個問題出現的,本篇文章主要是介紹 JSR303,Hibernate Validator 等校驗工具的使用,以及自定義校驗注解的使用。
校驗框架介紹
JSR303 是一套JavaBean參數校驗的標准,它定義了很多常用的校驗注解,我們可以直接將這些注解加在我們JavaBean的屬性上面,就可以在需要校驗的時候進行校驗了。注解如下:
Spring validtor 同樣擴展了jsr303,並實現了方法參數和返回值的校驗
Spring 提供了MethodValidationPostProcessor類,用於對方法的校驗
代碼實現
添加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; /** * 書名 */ @NotEmpty(message = "書名不能為空") private String bookName; /** * ISBN號 */ @NotNull(message = "ISBN號不能為空") private String bookIsbn; /** * 單價 */ @DecimalMin(value = "0.1",message = "單價最低為0.1") private double price; // getter setter ....... }
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 { /** * 添加value屬性,可以作為校驗時的條件,若不需要,可去掉此處定義 */ int value() default 0; String message() default "List集合中不能含有null元素"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; /** * 定義List,為了讓Bean的一個屬性上可以添加多套規則 */ @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER}) @Retention(RUNTIME) @Documented @interface 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> { private int value; @Override public void initialize(ListNotHasNull constraintAnnotation) { //傳入value 值,可以在校驗中使用 this.value = constraintAnnotation.value(); } public boolean isValid(List list, ConstraintValidatorContext constraintValidatorContext) { for (Object object : list) { if (object == null) { //如果List集合中含有Null元素,校驗失敗 return false; } } return true; } }
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 = {PersonAddView.class}, message = "添加用戶時地址不能為空") private String address; @Min(value = 18, groups = {PersonAddView.class}, message = "姓名不能低於18歲") @Max(value = 30, groups = {PersonModifyView.class}, message = "姓名不能超過30歲") private int 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()); } /** * 修改Person對象 * 此處啟用PersonModifyView 這個驗證規則 */ @RequestMapping(value = "/person", method = RequestMethod.PUT) public void modifyPerson(@RequestBody @Validated(value = {PersonModifyView.class}) Person person) { System.out.println(person.toString()); }
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/983836…
集成Bean Validation 1.1(JSR-349)到SpringMVC my.oschina.net/qjx1208/blo…
本文的全部代碼已上傳開源中國git倉庫: git.oschina.net/beiyan/Vali…
http://www.gitout.cn/?p=2532