jqueryspring(3.0)之後臺校驗

前一段試了一下前臺用jquery(1.3.2),後臺用spring(3.0),之間用json交換數據,
然後寫了篇總結jquery(1.3.2)<--json-->spring(3.0),有幾位大俠提出了後臺校驗
的問題,我也覺得這是很普遍的問題,就參考一些資料做了個小demo,現在總結一下,
歡迎各位拍磚。

我是這樣考慮的,在後臺接收數據時,首先使用一個所有屬性均爲String的對象,在
這個對象上,使用Bean Validation(JSR-303)進行數據校驗,校驗通過後,再將該對象
轉換爲一個VO然後進行後續處理,在校驗充分的情況下,轉換步驟是不會出現例外的。
在校驗失敗的時候,返回由屬性名(也即頁面元素名)、錯誤信息對組成的數組,前端
根據元素名自動突出顯示對應元素、並在其旁邊顯示錯誤信息,下面詳細說明。

1. 前端提交
這和前文一樣,沒什麼可說的。

2. 後臺校驗

2.1 屬性全爲String的對象,及其校驗
spring 3開始全面支持JSR-303,並以hibernate validator作爲默認實現,該技術可以
用聲明(Annotaion)的方式定義校驗,而且標記可以自由擴充,我覺得這很方便,所以
用的這一技術做的校驗。
後臺所有接收的對象都爲全String類型屬性對象,因爲對於json數據傳輸我們無法在接收
數據之前進行校驗,屬性全用String可以避免在Controller獲取數據前發生數據轉換錯誤。
JSR-303內建了一些校驗規則,例如Min,Max,Pattern(正則表達式,有了這個就可以
處理絕大部分校驗了),hibernate validtor還擴充了NotEmpty,Email等,一下是我的
例子中客戶信息的對象:
package json;

import javax.validation.constraints.Digits;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.Pattern;

import org.hibernate.validator.constraints.NotEmpty;
import org.hibernate.validator.constraints.Email;

import constraints.IsDate;

public class JSONCustomer {
@NotEmpty
private String name;

@NotEmpty
private String addr;

@NotEmpty
@IsDate(format="yyyy-MM-dd")
private String birthday;

@NotEmpty
private String hukou;

@Email
private String email;

@Pattern(regexp="(((\\w+)\\.)+(\\w+))?")
private String url;

@Digits(integer = 3, fraction = 0)
@Min(value = 100)
@Max(value = 230)
private String height;

//setter, getter略
}


其中IsDate是我自己擴充的校驗Annotation,這個後面再說。
這樣,校驗就定義好了,簡單吧。

2.2 校驗
下面來看看Controller中的校驗。首先要在Controller的構造函數中傳入validator,
spring有默認的validator,就是hibennate validator 4(所以需要這個jar包)。
然後在處理函數中使用validator.validate()就可以了,代碼如下:
package controller;

import java.lang.reflect.InvocationTargetException;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validator;

import org.apache.commons.beanutils.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import json.JSONCustomer;
import json.JSONResponse;
import vo.Customer;

@Controller
@RequestMapping("/customerInfo")
public class CustomerInfoController extends JSONController {

@Autowired
public CustomerInfoController(Validator validator) {
super(validator);
}

@RequestMapping(value = "/new", method = RequestMethod.POST)
@ResponseBody
public JSONResponse newCustomer(@RequestBody JSONCustomer jsoncustomer) throws IllegalAccessException, InvocationTargetException {
Set<ConstraintViolation<JSONCustomer>> failures = validator.validate(jsoncustomer);

if (failures.isEmpty()) {
//校驗通過
Customer customer = new Customer();
//將接收的全String屬性對象轉成vo
BeanUtils.copyProperties(jsoncustomer, customer);

return successed(jsoncustomer);
} else {
//校驗失敗
return failed(failures);
}
}
}


其中JSONController是我自己的Controller基類,後面再說。根據validate()的返回值failures
可以知道是否通過了校驗,successed()和failed()是JSONController的方法,爲了統一成功與
失敗時的返回數據格式,這個在後面說明。

2.3 自定義校驗Annotation
這部分請參閱相關的文檔,我就不多說了,這裏只貼出代碼:
package constraints;

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

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Target( { METHOD, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = IsDateValidator.class)
@Documented
public @interface IsDate {
String message() default "日期格式不正確";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String format();
}


package constraints;

import java.text.ParseException;
import java.text.SimpleDateFormat;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class IsDateValidator implements ConstraintValidator<IsDate, String> {
private String format;

public void initialize(IsDate constraintAnnotation) {
this.format = constraintAnnotation.format();
}

public boolean isValid(String object, ConstraintValidatorContext constraintContext) {
try {
SimpleDateFormat sf = new SimpleDateFormat(format);
sf.setLenient(false);
sf.parse((String)object);

return true;
} catch (ParseException pe) {
return false;
}
}

}


有了內建的校驗和自定義校驗的功能,我們就可以實現所有的校驗了。而且校驗的錯誤信息
是可以自定義的。校驗施加的對象也可以是屬性、方法、甚至對象整體,就是說可以根據對
象的多個屬性值判斷進行校驗,詳細的請參考jsr-303和其推薦實現hibernate validator的
相關文檔。

3. 返回值

3.1 返回的對象結構
爲了統一頁面端的處理,後臺返回值具有統一的結構,定義如下:
package json;

import java.util.ArrayList;

/**
* 服務器返回對象
* 所有服務器處理返回的統一對象
*/
public class JSONResponse {
//成功、失敗標誌
private boolean successed = false;
//錯誤信息
private ArrayList<JSONError> errors = null;
//成功時返回的對象
private Object returnObject = null;

//setter, getter略
}

其中JSONError定義如下:
package json;

/**
* 錯誤信息
*
*/
public class JSONError {
//元素名,與頁面元素名一致
private String element;
//錯誤信息
private String message;

//setter, getter略
}


成功時,successed=true,返回的對象在returnObject中,對於不同頁面,這個
對象是不同的,但各個頁面知道自己要得到一個什麼結構的數據。校驗失敗時
successed=false,errors是一個數組,其中每個元素是一個<元素名-錯誤信息>對。

3.2 返回方法
返回的方法是統一的,定義在JSONController裏,代碼如下:
package controller;

import java.util.ArrayList;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;

import json.JSONError;
import json.JSONResponse;


public class JSONController {
protected Validator validator;

public JSONController(Validator validator) {
this.validator = validator;
}

public JSONResponse successed(Object obj) {
JSONResponse ret = new JSONResponse();
ret.setSuccessed(true);
ret.setReturnObject(obj);

return ret;
}

public JSONResponse failed(Set failures) {
Set<ConstraintViolation<?>> failureSet = (Set<ConstraintViolation<?>>)failures;
ArrayList<JSONError> errors = new ArrayList<JSONError>();
for (ConstraintViolation<?> failure : failureSet) {
errors.add(new JSONError(failure.getPropertyPath().toString(), failure.getMessage()));
}

JSONResponse ret = new JSONResponse();
ret.setSuccessed(false);
ret.setErrors(errors);

return ret;
}
}


4. 頁面處理
頁面的html主要就是一個包含姓名、地址、生日、Email、身高等輸入項的form,這裏就
不貼出來丟人了,需要說明的是每個輸入項後都有一個<div class="errorMessage"></div>
準備放錯誤信息。
頁面以json格式接收後臺的處理結果,如果結果標識成功,則對返回的對象進行處理(顯
示之類的,這裏沒有涉及),如果標識失敗,則根據錯誤信息進行處理。
其中提交數據的處理代碼如下:
function save() {
var elemUserinfo = $('#customerInfo');
var userinfo = elemUserinfo.serializeObject();
var jsonuserinfo = JSON.stringify(userinfo);

jQuery.ajax( {
type : 'POST',
contentType : 'application/json',
url : 'customerInfo/new.do',
data : jsonuserinfo,
dataType : 'json',
success : function(resp) {
if (resp.successed) {
alert("數據已成功保存" );
//清除錯誤信息
elemUserinfo.applyErrors({});
} else {
elemUserinfo.applyErrors(resp.errors);
}
},
error : failed
});
};

其中的applyErrors是我自己寫的一段函數,就是根據錯誤信息的元素名,給元素畫個紅框,然後
把錯誤信息寫到該元素後面的<div class="errorMessage"></div>裏。該函數的參數爲空對象時
清除所有錯誤信息。代碼如下:

/**
* 突出顯示錯誤的輸入域,寫錯誤信息
*/
$.fn.applyErrors = function(es) {
alert('applyErrors - ' + es);
this.map(function() {
$.each(this.elements, function() {
var currElem = this;
$(currElem).css('border-color', ''); //清除突出顯示
$(currElem).nextAll('div').text(''); //清除錯誤信息
$.each(es, function() {
if (this.element == currElem.name) {
$(currElem).css('border-color', 'red'); //突出顯示
$(currElem).nextAll('div').text(this.message); //錯誤信息
}
})
})
});
};

我對javascript不太熟悉,這段代碼是仿照serializeObject寫的,哪位大俠有空,幫着改改,在下
不勝感激。

最後貼一張校驗結果的圖,我對美工不在行,畫面比較醜,見笑了。

[img]http://dl.iteye.com/upload/attachment/244664/4b79549e-23f7-36c2-8d29-0d5256bf7d0e.jpg[/img]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章