精华帖 (0) :: 良好帖 (3) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-05-05
前一段试了一下前台用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写的,哪位大侠有空,帮着改改,在下 不胜感激。 最后贴一张校验结果的图,我对美工不在行,画面比较丑,见笑了。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-05-05
楼主又更新了啊
直接上源代码吧 跑上次那个demo 我花了1个小时... |
|
返回顶楼 | |
发表时间:2010-05-05
对不起,代码在这里
|
|
返回顶楼 | |
发表时间:2010-05-05
看起来感觉不错啊 明天好好研究研究
|
|
返回顶楼 | |
发表时间:2010-05-06
在 Spring 出到 3.0.0 的时候,它内置的 Json 格式转换器还不支持 @DateTimeFormat 和 @NumberFormat,在 Ajax Simplifications in Spring 3.0 的这篇文章里作者自己写了一个 BeanPostProcessor 去解决这个问题。不知道 3.0.2 怎么样了
|
|
返回顶楼 | |
发表时间:2010-05-06
试试新的入例校验,之前都没作 只是在前端考虑了
|
|
返回顶楼 | |
发表时间:2010-05-06
还没怎么看过spring3.0 下来看看效果 谢谢lz
|
|
返回顶楼 | |
发表时间:2010-05-06
不错,但是在Chrome下不能用哦,还有在后台处理Json的请求和响应、验证也太繁琐了,为什么不像 Ajax Simplifications in Spring 3.0 那个sample那样处理呢?
|
|
返回顶楼 | |
发表时间:2010-05-06
我发现java注解还是挺有用的,哈哈。
|
|
返回顶楼 | |
发表时间:2010-05-06
java_doc 写道 不错,但是在Chrome下不能用哦,还有在后台处理Json的请求和响应、验证也太繁琐了,为什么不像 Ajax Simplifications in Spring 3.0 那个sample那样处理呢?
chrome里不行啊?我看看,我一直在firefox里试的 我觉得Ajax Simplifications in Spring 3.0里有个问题,如果前台的数据转不成Account会怎么样?例如一个需要数字的地方输入了字符,我觉得会在开始校验前就出例外了,结果会是导致ajax通讯失败,而这不是我们想要的,我们想要的是ajax通讯正常结束,但是接收到的是校验错误信息 |
|
返回顶楼 | |