源自:http://blog.csdn.net/tsingheng/article/details/42555307
下面提供一種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>
- <?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;
- }
- }
- @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原生支持的校驗註解也是有限的,如果要實現其他的驗證,可以自己拓展,拓展方法就下次再說了,我也纔剛接觸。