SpringMVC(五)資料驗證
SpringMVC(五)資料驗證
引數轉換出來後,緊跟著往往是需要驗證引數的合法性,因此SpringMVC也提供餓了驗證引數的機制。一方面,SpringMVC支援JSR-303註解驗證,在預設情況看下Spring Boot會引入關於Hibernate Validator
機制來支援JSR-303驗證規範;另外一方面,因為業務會比較複雜,所以需要自定義驗證規則。
JSR-303驗證
JSR-303主要是通過註解的方式進行的。這裡先定義一個需要驗證的POJO,此時需要在其屬性中加入相關的註解。
ValidatorPojo
package com.lay.mvc.pojo;
import org.hibernate.validator.constraints.Range;
import javax.validation.constraints.*;
import java.util.Date;
/**
* @Description:
* @Author: lay
* @Date: Created in 17:12 2018/11/13
* @Modified By:IntelliJ IDEA
*/
public class ValidatorPojo {
//非空判斷
@NotNull(message = "id不能為空")
private Long id;
//只能是將來日期
@Future(message = "需要一個將來日期")
//@Past //只能是過去的日期
@NotNull
private Date date;
@NotNull //不能為空
@DecimalMin(value = "0.1")//最小值0.1
@DecimalMax(value = "10000.00")//最大值10000.00
private Double doubleValue = null;
@NotNull //不能為空
@Min(value = 1,message = "最小值為1") //最小值為1
@Max(value = 88,message = "最大值為88") //最大值為88
private Integer integer;
@Range(min=1,max=888,message = "範圍為1到888")//限定範圍
private Long range;
@Email(message = "郵箱格式錯誤") //郵箱驗證
private String email;
@Size(min=20,max=30,message = "字串長度要求20到30之間")
private String size;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public Double getDoubleValue() {
return doubleValue;
}
public void setDoubleValue(Double doubleValue) {
this.doubleValue = doubleValue;
}
public Integer getInteger() {
return integer;
}
public void setInteger(Integer integer) {
this.integer = integer;
}
public Long getRange() {
return range;
}
public void setRange(Long range) {
this.range = range;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
}
為了測試驗證器,編寫一個前臺頁面,然後使用JSON的資料請求傳送這個物件給控制器。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta charset="UTF-8">
<title>測試JSR-303</title>
<script type="text/javascript" src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
//請求驗證的POJO
var pojo = {
id: null,
date: '2018-11-13',
doubleValue: 99999.09,
integer: 100,
range: 1000,
email: 'email',
size: 'testchangdu',
regexp: 'a,b,c,d'
}
$.ajax({
// 請求地址
url: "../valid/validate",
type:"post",
//告知傳遞引數型別為JSON
contentType:"application/json",
//將JSON轉為字串傳遞
data: JSON.stringify(pojo),
//成功後的方法
success: function (result) {
alert("success");
}
});
});
</script>
</head>
<body>
</body>
</html>
在頁面初始就使用Ajax傳送Json請求。
控制器
package com.lay.mvc.controller;
import com.lay.mvc.pojo.ValidatorPojo;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @Description:
* @Author: lay
* @Date: Created in 17:33 2018/11/13
* @Modified By:IntelliJ IDEA
*/
@Controller
public class ValidController {
/**
*
* @Description: 驗證頁面
* @param: []
* @return: java.lang.String
* @auther: lay
* @date: 17:34 2018/11/13
*/
@GetMapping("/valid/page")
public String validPage(){
return "validator/pojo";
}
/**
*
* @Description: 解析驗證引數錯誤
* @param: validatorPojo 需要驗證的POJO,使用註解@Valid表示驗證
* @param errors 錯誤資訊,它由Spring MVC通過驗證POJO後自動填充
* @return: java.util.Map<java.lang.String,java.lang.Object> 錯誤資訊
* @auther: lay
* @date: 17:43 2018/11/13
*/
@RequestMapping(value ="/valid/validate")
@ResponseBody
public Map<String,Object> validate(
@Valid @RequestBody ValidatorPojo validatorPojo, Errors errors){
Map<String,Object> errMap=new HashMap<>();
//獲取錯誤列表
List<ObjectError> oes=errors.getAllErrors();
for(ObjectError oe:oes){
String key=null;
String msg=null;
//欄位錯誤
if(oe instanceof FieldError){
FieldError fe= (FieldError) oe;
key=fe.getField();//獲取錯誤驗證欄位名
}else {
//非欄位錯誤
key=oe.getObjectName();//獲取驗證物件名稱
}
//錯誤資訊
msg=oe.getDefaultMessage();
errMap.put(key,msg);
}
return errMap;
}
}
瀏覽器輸入:http://localhost:8080/valid/page,
然後F12開啟 控制檯——Network(網路)——XHR(檢視非同步請求)——Response(返回),就會看到下面的json資料集
{
"date": "需要一個將來日期",
"doubleValue": "必須小於或等於10000.00",
"email": "郵箱格式錯誤",
"id": "id不能為空",
"integer": "最大值為88",
"range": "範圍為1到888",
"size": "字串長度要求20到30之間"
}
引數驗證機制
為了能夠更加靈活地提供驗證機制,Spring孩童了自己的驗證機制。在引數轉換時,可以看到在SpringMVC中,存在WebDataBinder
機制進行管理,在預設情況下Spring會自動地根據上下文通過註冊了的轉換器轉你出控制器所需要的引數。除了可以註冊轉換器外,還允許註冊驗證器(Validator)。
在Spring控制器中,它還允許使用註解@InitBinder
,這個註解的作用是允許在進入控制器的方法前修改WebDataBinder
機制。
Validator介面原始碼
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.validation;
public interface Validator {
/**
* 判定當前驗證器是否支援該Class型別的驗證
* @param clazz pojo型別
* @return 當前驗證器是否支援該POJO驗證
*/
boolean supports(Class<?> var1);
/**
* 如果supports返回true,則這個方法執行驗證邏輯
* @param target 被驗證的POJO物件(已繫結引數)
* @param errors 錯誤物件
*/
void validate(Object target, Errors errors);
}
它定義了兩個方法,其中supports方法引數為需要驗證的POJO型別,如果該方法返回true,則Spring會使用當前驗證器的validate方法去驗證pojo,而validate方法包含需要的target物件和錯誤物件errors,其中target是繫結引數後的pojo,這樣便可以通過這個引數物件進行業務邏輯的自定義驗證。如果發現有錯誤,則可以儲存到errors物件中,然後返回給控制器。
自定義使用者驗證器
PersonValidator
package com.lay.mvc.validator;
import com.lay.mvc.entity.Person;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
/**
* @Description:
* @Author: lay
* @Date: Created in 18:14 2018/11/13
* @Modified By:IntelliJ IDEA
*/
public class PersonValidator implements Validator {
//該驗證器支援Person類的驗證
@Override
public boolean supports(Class<?> clazz) {
return clazz.equals(Person.class);
}
//驗證邏輯
@Override
public void validate(Object target, Errors errors) {
//物件為空
if(target==null){
//直接在引數處報錯,這樣就不能進入控制器的方法
errors.rejectValue("",null,"使用者不能為空");
return;
}
//強制轉換
Person person=(Person)target;
//使用者名稱非空串
if(StringUtils.isEmpty(person.getPersonName())){
//增加錯誤,可以進入控制器方法
errors.rejectValue("personName","null","使用者名稱不能為空");
}
}
}
有了這個驗證器,Spring還不會自動啟用它,因為還美譽哦繫結給WebDataBinder
機制。在Spring MVC中提供了一個註解@InitBinder
,它的作用是在執行控制器的方法前,處理器會先執行@InitBinder
標註的方法。這時可以將WebDataBinder
物件作為引數傳遞到方法中,通過這層關係得到WebDataBinder
物件,這個物件有個setValidator
方法,它可以繫結自定義的驗證器,這樣就可以在獲取引數之後,通過自定義的驗證器去驗證引數,只是WebDataBinder
除了可以繫結驗證器外,還可以進行引數的自定義,例如,不使用@DataTimeFormat
獲取日期引數。
控制器
/**
*
* @Description:呼叫控制器前執行這個方法
* @param: [webDataBinder]
* @return: void
* @auther: lay
* @date: 18:39 2018/11/13
*/
@InitBinder
public void initBinder(WebDataBinder webDataBinder){
//繫結驗證器
webDataBinder.setValidator(new PersonValidator());
//定義日期格式引數,引數不再需要註解@DateTimeFormat,boolean引數表示是否允許為空
webDataBinder.registerCustomEditor(Date.class,new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),false));
}
/**
*
* @Description: 解析驗證引數錯誤
* @param: validatorPojo 需要驗證的POJO,使用註解@Valid表示驗證
* @param errors 錯誤資訊,它由Spring MVC通過驗證POJO後自動填充
* @return: java.util.Map<java.lang.String,java.lang.Object> 錯誤資訊
* @auther: lay
* @date: 17:43 2018/11/13
*/
@RequestMapping(value ="/valid/personValid")
@ResponseBody
public Map<String,Object> personValid(
@Valid Person person, Errors errors,Date date){
Map<String,Object> map=new HashMap<>();
map.put("person",person);
map.put("date",date);
//獲取錯誤列表
List<ObjectError> oes=errors.getAllErrors();
for(ObjectError oe:oes){
String key=null;
String msg=null;
//欄位錯誤
if(oe instanceof FieldError){
FieldError fe= (FieldError) oe;
key=fe.getField();//獲取錯誤驗證欄位名
}else {
//非欄位錯誤
key=oe.getObjectName();//獲取驗證物件名稱
}
//錯誤資訊
msg=oe.getDefaultMessage();
map.put(key,msg);
}
return map;
}
瀏覽器輸入:http://localhost:8080/valid/personValid?person=1--note_1&date=2018-11-13
這裡userName為空。
結果
{
"date": "2018-11-12T16:00:00.000+0000",
"person": {
"id": 1,
"note": "note_1",
"personName": ""
},
"personName": "使用者名稱不能為空"
}