jquery -- json -- spring(3.0)之后台校验

来源:互联网 发布:广西金岸网络科技集团 编辑:程序博客网 时间:2024/05/17 01:04

前一段试了一下前台用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写的,哪位大侠有空,帮着改改,在下

不胜感激。