该示例项目使用SpringBoot,添加web和aop依赖。 SpringMVC最常用的校验是对一个javaBean的校验,默认使用hibernate-validator校验框架。而网上对校验单个参数,譬如String,int之类的资料极少,这一篇就是讲这个的。
定义了@NotEmpty等标签后,结合在Controller里使用@Valid即可完成参数的校验。
package com.example.controller; import com.example.bean.User; import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.Range; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.validation.Valid; /** * Created by wuwf on 17/4/27. * */ @RestController public class FirstController { @RequestMapping("/first") public Object first(@Valid User user, BindingResult bindingResult) { return "first controller"; } @RequestMapping("/second") public @Length Object second(@Length(min = 6, message = "密码长度不能小于6位") String password) { return "second controller"; } @RequestMapping("/third") public @Length Object third(@Range(min = 6, max = 10, message = "数据需要大于6小于10") int num, @Length(min = 6, message = "密码长度不能小于6位") String password) { return "third controller"; } @RequestMapping("/four") public @Length Object four(int page) { return "four controller"; } }譬如first方法里,只需要加上@Valid标签即可完成校验。如果校验不通过,那么错误信息就会封装到BindingResult对象了,可以通过bindingResult的相关方法获取详细的错误信息并返回给用户。 访问:http://localhost:8080/first?name=1&password=1 debug可看到 如果不加BindingResult则会抛出异常。 此时即可完成表单类,或者用户注册之类的类似请求的参数校验了,可以选择获取bindingResult信息后直接return给用户。如果这样的需要校验的地方比较多,每个都单独处理比较麻烦,可以通过aop统一处理返回,后面会讲到。 校验的标签可参考:http://blog.csdn.net/catoop/article/details/51278675 相关文章:http://412887952-qq-com.iteye.com/blog/2312356
在很多场景下,我们不需要校验一个javaBean,更多的是校验单个的int,String等。也就是controller里的second和third方法。像方法中写的那样,但是直接写上去,是不起作用的,校验框架并没有去校验,我们需要做的就是让它生效。 参考如下:https://diamondfsd.com/article/78fa12cd-b530-4a90-b438-13d5a0c4e26c http://blog.csdn.net/catoop/article/details/51284638 直接上代码吧
package com.example.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.hibernate.validator.internal.engine.path.PathImpl; import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.stereotype.Component; import org.springframework.validation.BeanPropertyBindingResult; import org.springframework.validation.ObjectError; import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.ValidatorFactory; import javax.validation.executable.ExecutableValidator; import java.lang.reflect.Method; import java.util.List; import java.util.Set; /** * Created by wuwf on 17/4/27. * 参数校验切面 */ @Aspect @Component public class ValidAspect { private ObjectError error; @Pointcut("execution(public * com.example.controller.*.*(..))") public void valid() { } //环绕通知,环绕增强,相当于MethodInterceptor @Around("valid()") public Object arround(ProceedingJoinPoint pjp) { System.out.println("方法环绕start....."); try { //取参数,如果没参数,那肯定不校验了 Object[] objects = pjp.getArgs(); if (objects.length == 0) { return pjp.proceed(); } /**************************校验封装好的javabean**********************/ //寻找带BindingResult参数的方法,然后判断是否有error,如果有则是校验不通过 for (Object object : objects) { if (object instanceof BeanPropertyBindingResult) { //有校验 BeanPropertyBindingResult result = (BeanPropertyBindingResult) object; if (result.hasErrors()) { List<ObjectError> list = result.getAllErrors(); for (ObjectError error : list) { System.out.println(error.getCode() + "---" + error.getArguments() + "--" + error.getDefaultMessage()); //返回第一条校验失败信息。也可以拼接起来返回所有的 return error.getDefaultMessage(); } } } } /**************************校验普通参数*************************/ // 获得切入目标对象 Object target = pjp.getThis(); // 获得切入的方法 Method method = ((MethodSignature) pjp.getSignature()).getMethod(); // 执行校验,获得校验结果 Set<ConstraintViolation<Object>> validResult = validMethodParams(target, method, objects); //如果有校验不通过的 if (!validResult.isEmpty()) { String[] parameterNames = parameterNameDiscoverer.getParameterNames(method); // 获得方法的参数名称 for(ConstraintViolation<Object> constraintViolation : validResult) { PathImpl pathImpl = (PathImpl) constraintViolation.getPropertyPath(); // 获得校验的参数路径信息 int paramIndex = pathImpl.getLeafNode().getParameterIndex(); // 获得校验的参数位置 String paramName = parameterNames[paramIndex]; // 获得校验的参数名称 System.out.println(paramName); //校验信息 System.out.println(constraintViolation.getMessage()); } //返回第一条 return validResult.iterator().next().getMessage(); } return pjp.proceed(); } catch (Throwable e) { e.printStackTrace(); return null; } } private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); private final ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); private final ExecutableValidator validator = factory.getValidator().forExecutables(); private <T> Set<ConstraintViolation<T>> validMethodParams(T obj, Method method, Object[] params) { return validator.validateParameters(obj, method, params); } }注释写的比较清楚了,这是一个aop切面类,拦截请求,并获取方法里的所有参数。第65行到85行是对普通参数进行校验的。 加上这几行代码后在controller里写的那些才会生效,并且把校验信息保存到了ConstraintViolation的Set集合里,判断Set是否有值,即可知道是否有校验不通过的信息,然后就可以取到校验信息并返回给用户,然后结束方法。 而49到62行,主要是判断是否存在BindingResult参数,如果有,说明有校验javaBean的意图,如果BindingResult有值,说明存在校验不通过的信息,那么就可以做处理了。通过这样的aop切面,就可以不用在每个controller方法里去处理校验信息了。