下面提供一种springmvc的校验方案,一般没有校验或者手动写validator的话都要写好多代码好多if判断,使用JSR-303规范校验只需要在Pojo字段上加上相应的注解就可以实现校验了
1.依赖的jar包,我直接贴pom了
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>4.1.1.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.4.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.4.0</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>1.1.0.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.1.3.Final</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.7</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.4</version> </dependency> <dependency> <groupId>javax.el</groupId> <artifactId>javax.el-api</artifactId> <version>3.0.0</version> </dependency> </dependencies>
2.springmvc配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 默认的注解映射的支持 --> <mvc:annotation-driven/> <context:annotation-config/> <aop:aspectj-autoproxy/> <!-- 自动扫描的包名 --> <context:component-scan base-package="spring.test.web.controller"/> <bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" /> </beans>
3.写java代码了
先建一个User类
public class User { @NotNull private String username; @NotNull(message = "密码不能为空") private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
然后建一个controller试试
@ResponseBody @RequestMapping("/test1") public AjaxResponse validateTest1(@Valid User user, BindingResult result){ AjaxResponse ajaxResponse = new AjaxResponse(); Map<String, Object> map = new HashMap<>(); if(result.hasErrors()){ ajaxResponse.setSuccess(false); ajaxResponse.setHasErrors(true); map.put("errors", result.getAllErrors()); } else { map.put("now", new Date()); map.put("user", user); } ajaxResponse.setData(map); return ajaxResponse; }
这个方法第一个参数上加了@Valid标注,第二个参数是必须的而且必须紧跟着@Valid参数之后,校验结果都在这个result里面
方法逻辑比较简单,调用result.hasErrors()看校验有没有错误,有错误的话就把所有的错误放进结果返回给客户端,如果没有错误就返回当前时间跟user对象
然后写个测试方法试试
@Test public void test1() throws Exception{ this.mockMvc.perform(post("/validator/test1")).andDo(print()); this.mockMvc.perform(post("/validator/test1").param("username", "testusername").param("password", "testpassword")).andDo(print()); }
第一个请求的结果是
{"success":false,"message":null,"hasErrors":true,"data":[{"name":"user","message":"may not be null"},{"name":"user","message":"密码不能为空"}]}
第二个请求结果是
{"success":true,"message":null,"hasErrors":false,"data":{"now":1420788232169,"user":{"username":"testusername","password":"testpassword"}}}
很明显第一次请求没有用户名和密码两个字段校验都没有通过,第二次是成功的
用起来还是很简单的,但是如果我不想每次都在controller方法里写if(result.hasErrors())怎么办呢,写个切面,把if写在切面里,如果有errors直接就返回了,不用再执行controller方法了
@Aspect public class ValidAspect { @Autowired private Validator validator; @Around("@annotation(org.springframework.web.bind.annotation.ResponseBody)") public Object doTest(ProceedingJoinPoint pjp) throws Throwable{ MethodSignature signature = (MethodSignature) pjp.getSignature(); Method method = signature.getMethod(); if(!AjaxResponse.class.equals(method.getReturnType())){ pjp.proceed(); } Object[] args = pjp.getArgs(); Annotation[][] annotations = method.getParameterAnnotations(); for(int i = 0; i < annotations.length; i++){ if(!hasValidAnnotation(annotations[i])){ continue; } if(!(i < annotations.length-1 && args[i+1] instanceof BindingResult)){ //验证对象后面没有跟bindingResult,事实上如果没有应该到不了这一步 continue; } BindingResult result = (BindingResult) args[i+1]; if(result.hasErrors()){ AjaxResponse ajaxResponse = new AjaxResponse(); ajaxResponse.setSuccess(false); ajaxResponse.setHasErrors(true); ajaxResponse.setData(processErrors(result)); return ajaxResponse; } } return pjp.proceed(); } private boolean hasValidAnnotation(Annotation[] annotations){ if(annotations == null){ return false; } for(Annotation annotation : annotations){ if(annotation instanceof Valid){ return true; } } return false; } private List<BindingError> processErrors(BindingResult result){ if(result != null && result.hasErrors()){ List<BindingError> list = new ArrayList<BindingError>(); for(ObjectError error : result.getAllErrors()){ BindingError be = new BindingError(); be.setMessage(error.getDefaultMessage()); be.setName(error.getObjectName()); list.add(be); } return list; } return null; } }
注意要在springmvc配置文件加一行<bean class="spring.test.web.aop.ValidAspect"/>
然后再写个方法逻辑跟上面的类似
@ResponseBody @RequestMapping("/test2") public AjaxResponse validateTest2(@Valid User user, BindingResult result){ AjaxResponse ajaxResponse = new AjaxResponse(); Map<String, Object> map = new HashMap<>(); map.put("now", new Date()); map.put("user", user); ajaxResponse.setData(map); return ajaxResponse; }
这里没有再去判断hasErrors()了,然后测试一下
写测试方式试一下
@Test public void test2() throws Exception{ this.mockMvc.perform(post("/validator/test2")).andDo(print()); this.mockMvc.perform(post("/validator/test2").param("username", "testusername").param("password", "testpassword")).andDo(print()); }
第一个请求结果是
{"success":false,"message":null,"hasErrors":true,"data":[{"name":"user","message":"密码不能为空"},{"name":"user","message":"may not be null"}]}
第二个请求结果是
{"success":true,"message":null,"hasErrors":false,"data":{"now":1420788479105,"user":{"username":"testusername","password":"testpassword"}}}
效果跟上面是一样的
当然我这个切面仅仅是个示范,我拦截的是带有ResponseBody注解的方法,我这些方法会返回一个统一的格式,如果是要返回别的视图就要自己定义注解加其他参数了
JSR-303原生支持的校验注解也是有限的,如果要实现其他的验证,可以自己拓展,拓展方法就下次再说了,我也才刚接触。