当前位置: 代码迷 >> JavaScript >> SpringMVC 施用JSR-303进行校验 @Valid
  详细解决方案

SpringMVC 施用JSR-303进行校验 @Valid

热度:961   发布时间:2012-09-21 15:47:26.0
SpringMVC 使用JSR-303进行校验 @Valid

?

一、准备校验时使用的JAR

?????

? 说明:

???????? validation-api-1.0.0.GA.jar是JDK的接口;

???????? hibernate-validator-4.2.0.Final.jar是对上述接口的实现。

?

------------------------------------------------------------

新增一个测试的pojo bean ,增加jsr 303格式的验证annotation

Java代码 复制代码 ?收藏代码
	@NotEmpty
	private String userName;
	@Email
	private String email;


在controller 类中的handler method中,对需要验证的对象前增加@Valid 标志

Java代码 复制代码 ?收藏代码
	@RequestMapping("/valid")
	public String valid(@ModelAttribute("vm") [color=red]@Valid[/color] ValidModel vm, BindingResult result) {
		if (result.hasErrors()) {
			return "validResult";
		}

		return "helloworld";
	}


增加显示验证结果的jsp如下

Jsp代码 复制代码 ?收藏代码
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>

<html>
<head>
<title>Reservation Form</title>
<style>
.error {
	color: #ff0000;
	font-weight: bold;
}
</style>
</head>

<body>
	<form:form method="post" modelAttribute="vm">
		<form:errors path="*" cssClass="error" />
		<table>
			<tr>
				<td>Name</td>
				<td><form:input path="userName" />
				</td>
				<td><form:errors path="userName" cssClass="error" />
				</td>
			</tr>
			<tr>
				<td>email</td>
				<td><form:input path="email" />
				</td>
				<td><form:errors path="email" cssClass="error" />
				</td>
			</tr>
	
			<tr>
				<td colspan="3"><input type="submit" />
				</td>
			</tr>
		</table>
	</form:form>
</body>
</html>


访问 http://localhost:8080/springmvc/valid?userName=winzip&email=winzip
查看验证结果。
二:自定义jsr 303格式的annotation
参考hibernate validator 4 reference 手册中3.1节,增加一个自定义要求输入内容为定长的annotation验证类
新增annotation类定义

Java代码 复制代码 ?收藏代码
@Target( { METHOD, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = FixLengthImpl.class)
public @interface FixLength {

	int length();
	String message() default "{net.zhepu.web.valid.fixlength.message}";

	Class<?>[] groups() default {};

	Class<? extends Payload>[] payload() default {};
}


及具体的验证实现类如下

Java代码 复制代码 ?收藏代码
public class FixLengthImpl implements ConstraintValidator<FixLength, String> {
	private int length;
	@Override
	public boolean isValid(String validStr,
			ConstraintValidatorContext constraintContext) {
		if (validStr.length() != length) {
			return false;
		} else {
			return true;
		}
	}

	@Override
	public void initialize(FixLength fixLen) {
		this.length = fixLen.length();
	}
}


为使自定义验证标注的message正常显示,需要修改servlet context配置文件,新增messageSource bean,如下

Xml代码 复制代码 ?收藏代码
  1. ??
	<bean id="messageSource"
		class="org.springframework.context.support.ReloadableResourceBundleMessageSource"
		p:fallbackToSystemLocale="true" p:useCodeAsDefaultMessage="false"
		p:defaultEncoding="UTF-8">
		<description>Base message source to handle internationalization
		</description>
		<property name="basenames">
			<list>
				<!-- main resources -->
				<value>classpath:valid/validation</value>
			</list>
		</property>
	</bean>


表示spring 将从路径valid/validation.properties中查找对于的message。
新增valid bean 引用新增的messageSource bean,表示valid message将从messageSource bean 注入。

Xml代码 复制代码 ?收藏代码
	<bean id="validator"
		class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"
		p:validationMessageSource-ref="messageSource">
		<description>Enable the bean validation provider, and configure it to
			use the messageSource when resolving properties</description>
	</bean>



修改 <mvc:annotation-driven> 增加对validator bean的引用

Xml代码 复制代码 ?收藏代码
<mvc:annotation-driven validator="validator" />



最后修改之前新增的pojo bean ,新增一个mobileNO属性并增加对自定义标注的引用

Java代码 复制代码 ?收藏代码
	@FixLength(length=11)
	private String mobileNO;


在前端jsp中也增加新字段的支持

Jsp代码 复制代码 ?收藏代码
			<tr>
				<td>mobileno</td>
				<td><form:input path="mobileNO" />
				</td>
				<td><form:errors path="mobileNO" cssClass="error" />
				</td>
			</tr>



可访问url http://localhost:8080/springmvc/valid?userName=winzip&email=winzip&mobileNO=138188888
来查看修改的结果。

三 json输入的验证
Spring mvc 3.0.5中对于json格式的输入直接使用@valid标注有问题,目前这个bug还未修复 (见 SPR-6709),预计在3.1 m2版本中会修复。
在此之前,可以通过如下几种方式来对json(或xml)格式的输入来进行验证。
1:在handler method中直接对输入结果进行验证

Java代码 复制代码 ?收藏代码
    	@RequestMapping("/validJson1")
    	@ResponseBody
    	public JsonResult processSubmitjson(@RequestBody ValidModel vm,
    			HttpServletRequest request) {
    		JsonResult jsonRst = new JsonResult();
    		
    		Set<ConstraintViolation<ValidModel>> set = validator.validate(vm);
    		for (ConstraintViolation<ValidModel> violation : set) {
    
    			String propertyPath = violation.getPropertyPath().toString();
    			;
    			String message = violation.getMessage();
    			log.error("invalid value for: '" + propertyPath + "': "
    					+ message);
    		}		
    		if (!set.isEmpty()){
    			jsonRst.setSuccess(false);
    			jsonRst.setMsg("输入有误!");
    			return jsonRst;
    		}
    
    		jsonRst.setSuccess(true);
    		jsonRst.setMsg("输入成功!");
    		return jsonRst;
    	}




    可通过修改后的helloworld.jsp中的json valid test1按钮进行调用测试。

    2:将此验证逻辑封装为一个AOP,当需验证的对象前有@valid标注和@RequestBody标注时开始验证
    新增handler method如下

    Java代码 复制代码 ?收藏代码
         @RequestMapping("/validJson2")
          @ResponseBody
          public JsonResult testJson4(@RequestBody @Valid ValidModel vm){
          	log.info("handle json for valid");
          	return new JsonResult(true,"return ok");   	
          }


      这里没有对输入值做任何验证,所有的验证都在AOP中完成。
      修改pom.xml增加对AOP相关类库的引用。

      Xml代码 复制代码 ?收藏代码
      		<dependency>
      			<groupId>org.aspectj</groupId>
      			<artifactId>aspectjweaver</artifactId>
      			<version>1.6.11</version>
      		</dependency>
      
      		<dependency>
      			<groupId>cglib</groupId>
      			<artifactId>cglib</artifactId>
      			<version>2.2.2</version>
      		</dependency>
      		
      		<dependency>
      			<groupId>org.springframework</groupId>
      			<artifactId>spring-aop</artifactId>
      			<version>${org.springframework.version}</version>
      		</dependency>


      修改servet context xml,增加对aop的支持。

      Xml代码 复制代码 ?收藏代码
        	<!-- enable Spring AOP support -->
        	<aop:aspectj-autoproxy proxy-target-class="true" />


        最后,新增AOP类

        Java代码 复制代码 ?收藏代码
        public class CustomerValidatorAOP {
          private Validator validator;
         
          @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
          private void controllerInvocation() {
          }
         
          @Around("controllerInvocation()")
          public Object aroundController(ProceedingJoinPoint joinPoint) throws Throwable {
         
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            Method method = methodSignature.getMethod();
            Annotation[] annotationList = method.getAnnotations();
           /* for(Annotation anno:annotationList){
            	System.out.println(ResponseBody.class.isInstance(anno));
            }
        */
            Annotation[][] argAnnotations = method.getParameterAnnotations();
            String[] argNames = methodSignature.getParameterNames();
            Object[] args = joinPoint.getArgs();
         
            for (int i = 0; i < args.length; i++) {
              if (hasRequestBodyAndValidAnnotations(argAnnotations[i])) {
                Object ret = validateArg(args[i], argNames[i]);
                if(ret != null){
                	return ret;
                }
              }
            }
         
            return joinPoint.proceed(args);
          }
         
          private boolean hasRequestBodyAndValidAnnotations(Annotation[] annotations) {
            if (annotations.length < 2)
              return false;
         
            boolean hasValid = false;
            boolean hasRequestBody = false;
         
            for (Annotation annotation : annotations) {
              if (Valid.class.isInstance(annotation))
                hasValid = true;
              else if (RequestBody.class.isInstance(annotation))
                hasRequestBody = true;
         
              if (hasValid && hasRequestBody)
                return true;
            }
            return false;
          }
         
        
          private JsonResult validateArg(Object arg, String argName) {
            BindingResult result = getBindingResult(arg, argName);
            validator.validate(arg, result);
            if (result.hasErrors()) {
              JsonResult jsonresult = new JsonResult();
              jsonresult.setSuccess(false);
              jsonresult.setMsg("fail");
              return jsonresult;
            }
            return null;
          }
         
          private BindingResult getBindingResult(Object target, String targetName) {
            return new BeanPropertyBindingResult(target, targetName);
          }
         
          @Required
          public void setValidator(Validator validator) {
            this.validator = validator;
          }


        这里只考虑了输入为json格式的情况,仅仅作为一种思路供参考,实际使用时需要根据项目具体情况进行调整。
        可通过修改后的helloworld.jsp中的json valid test2按钮进行调用测试。

        ?

        原文:http://starscream.iteye.com/blog/1068905

          相关解决方案