Java Bean Validation 引數校驗
引數校驗是我們程式開發中必不可少的過程。使用者在前端頁面上填寫表單時,前端js程式會校驗引數的合法性,當資料到了後端,為了防止惡意操作,保持程式的健壯性,後端同樣需要對資料進行校驗。後端引數校驗最簡單的做法是直接在業務方法裡面進行判斷,當判斷成功之後再繼續往下執行。但這樣帶給我們的是程式碼的耦合,冗餘。當我們多個地方需要校驗時,我們就需要在每一個地方呼叫校驗程式,導致程式碼很冗餘,且不美觀。
那麼如何優雅的對引數進行校驗呢?JSR303就是為了解決這個問題出現的,本篇文章主要是介紹 JSR303,Hibernate Validator 等校驗工具的使用,以及自定義校驗註解的使用。
校驗框架介紹
JSR303 是一套JavaBean引數校驗的標準,它定義了很多常用的校驗註解,我們可以直接將這些註解加在我們JavaBean的屬性上面,就可以在需要校驗的時候進行校驗了。註解如下:
Hibernate validator 在JSR303的基礎上對校驗註解進行了擴充套件,擴充套件註解如下:
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/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
https://www.cnblogs.com/beiyan/p/5946345.html