本示例使用JDK1.8, Spring Boot 1.5.9.RELEASE, hibernate-validator-5.3.6.Final。
1.功能需求 在提交的表单服务端需要验证提交的表单,验证的主要功能主要有:
对提交的表单字段进行常用验证 例如:字段是否为空,字段长度
自定义错误信息,消息的国际化支持 例如:需要显示个性的消息
自定义验证方式 例如:需要根据一定的逻辑验证
假设: 现有一学生对象,需要进去添加操作,需要对其字段有名字,年龄和生日,先有如下验证: a.所有字段必须有值 b.年龄在18-130岁 c.名字不能重复
2.常用验证 hibernate-validator-.jar 和 validation-api- .jar 有为我们提供一些常用的验证注解:
1 2 3 4 @NotNull @NotBlank @Size(min=, max=) ......
更多方法:https://docs.jboss.org/hibernate/validator/5.3/reference/en-US/html_single/
3.项目结构 针对如上的需求,创建 spring-boot-hibernate-validation-demo 项目:
4.自定义验证注解 1.定义业务逻辑的类和方法:StudentService.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.devnp.springboothibernatevalidationdemo.service;import org.springframework.stereotype.Component;@Component("studentService") public class StudentService { public boolean nameUniqueCheck (String name) { if ("Tom" .equals(name)) return false ; return true ; } }
2.创建验证的注解:StudentNameUnique.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.devnp.springboothibernatevalidationdemo.validation;import static java.lang.annotation.ElementType.FIELD;import static java.lang.annotation.ElementType.METHOD;import static java.lang.annotation.RetentionPolicy.RUNTIME;import java.lang.annotation.Retention;import java.lang.annotation.Target;import javax.validation.Constraint;import javax.validation.Payload;@Target({ METHOD, FIELD}) @Retention(RUNTIME) @Constraint(validatedBy = {StudentNameUniqueValidation.class }) public @interface StudentNameUnique { String message () default "{stu.name.unique}" ; Class<?>[] groups() default { }; Class<? extends Payload >[] payload() default { }; }
3.实现验证:StudentNameUniqueValidation.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package com.devnp.springboothibernatevalidationdemo.validation;import javax.validation.ConstraintValidator;import javax.validation.ConstraintValidatorContext;import org.springframework.beans.factory.annotation.Autowired;import com.devnp.springboothibernatevalidationdemo.service.StudentService;public class StudentNameUniqueValidation implements ConstraintValidator <StudentNameUnique, Object>{ @Autowired private StudentService studentService ; @Override public void initialize (StudentNameUnique constraintAnnotation) { System.out.println("Student Name Unique Validation Init..." ); } @Override public boolean isValid (Object value, ConstraintValidatorContext context) { return studentService.nameUniqueCheck((String) value); } }
5.验证对象 Object 先有一学生对象,对其的属性设置相应注解:StudentDto.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 package com.devnp.springboothibernatevalidationdemo.dto;import java.io.Serializable;import java.util.Date;import javax.validation.constraints.NotNull;import org.hibernate.validator.constraints.NotBlank;import org.hibernate.validator.constraints.Range;import com.devnp.springboothibernatevalidationdemo.validation.StudentNameUnique;public class StudentDto implements Serializable { private static final long serialVersionUID = 1L ; @NotBlank(message="{stu.name.not.blank}") @StudentNameUnique private String stuName ; @Range(min= 18, max=130, message="{stu.age.range}") @NotNull(message="{stu.age.not.blank}") private Integer stuAge ; @NotNull(message="{stu.birth.not.blank}") private Date stuBirth ; public StudentDto () { super (); } public StudentDto (String stuName, Integer stuAge, Date stuBirth) { super (); this .stuName = stuName; this .stuAge = stuAge; this .stuBirth = stuBirth; } public String getStuName () { return stuName; } public void setStuName (String stuName) { this .stuName = stuName; } public Integer getStuAge () { return stuAge; } public void setStuAge (Integer stuAge) { this .stuAge = stuAge; } public Date getStuBirth () { return stuBirth; } public void setStuBirth (Date stuBirth) { this .stuBirth = stuBirth; } @Override public String toString () { return "StudentDto [stuName=" + stuName + ", stuAge=" + stuAge + ", stuBirth=" + stuBirth + "]" ; } }
6.消息配置 关于Spring Boot对国际化的配置:Spring Boot 国际化 (Spring Boot Internationalization Demo)
1..properties文件
StudentMessages.properties
1 2 3 4 5 6 7 return.msg = 请求成功 stu.name.not.blank = 学生姓名不能为空 stu.name.unique = 学生姓名不能重复 stu.age.not.blank = 学生年龄不能为空 stu.age.range = 学生年龄应该在18到130岁 stu.birth.not.blank = 学生生日不能为空
StudentMessages_zh_CN.properties
1 2 3 4 5 6 7 return.msg = 请求成功 stu.name.not.blank = 学生姓名不能为空 stu.name.unique = 学生姓名不能重复 stu.age.not.blank = 学生年龄不能为空 stu.age.range = 学生年龄应该在18到130岁 stu.birth.not.blank = 学生生日不能为空
StudentMessages_en_US.properties
1 2 3 4 5 6 7 return.msg = Success stu.name.not.blank = Student Name cannot be blank. stu.name.unique = Student Name must be Unique. stu.age.not.blank = Student Age cannot be blank. stu.age.range = Student Age should be 18 to 130. stu.birth.not.blank = Student Birth cannot be blank.
2.配置 将消息文件添加到Spring Boot中:
1 2 3 4 5 spring: messages: fallback-to-system-locale: false encoding: UTF-8 basename: com/devnp/message/StudentMessages
设置加载和国际化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package com.devnp.springboothibernatevalidationdemo.config;import java.util.Locale;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.MessageSource;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.validation.Validator;import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;import org.springframework.web.servlet.LocaleResolver;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;import org.springframework.web.servlet.i18n.SessionLocaleResolver;@Configuration public class MessageConfig extends WebMvcConfigurerAdapter { @Autowired private MessageSource messageSource; @Bean public LocalValidatorFactoryBean validator () { LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean (); localValidatorFactoryBean.setValidationMessageSource(messageSource); return localValidatorFactoryBean; } @Override public Validator getValidator () { return validator(); } @Bean public LocaleResolver localeResolver () { SessionLocaleResolver slr = new SessionLocaleResolver (); slr.setDefaultLocale(Locale.CHINESE); return slr; } @Bean public LocaleChangeInterceptor localeChangeInterceptor () { LocaleChangeInterceptor lci = new LocaleChangeInterceptor (); lci.setParamName("lang" ); return lci; } @Override public void addInterceptors (InterceptorRegistry registry) { registry.addInterceptor(localeChangeInterceptor()); } }
7.控制器 Controller 表单提交的控制器:StudentController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package com.devnp.springboothibernatevalidationdemo.web;import javax.validation.Valid;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.MessageSource;import org.springframework.context.i18n.LocaleContextHolder;import org.springframework.validation.BindingResult;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.RestController;import com.devnp.springboothibernatevalidationdemo.dto.StudentDto;@RestController @RequestMapping("/student") public class StudentController { @Autowired private MessageSource messageSource; @RequestMapping(path="/add", method= {RequestMethod.POST}) public String add (@Valid @RequestBody StudentDto studentDto, BindingResult bindingResult) { if (bindingResult.hasErrors()) { bindingResult.getAllErrors().stream().forEach(error -> {System.out.println(error.getDefaultMessage());}); } System.out.println(studentDto); return messageSource.getMessage("return.msg" , null , LocaleContextHolder.getLocale()) ; } }
8.测试 撰写单元测试的类和方法:StudentControllerTests.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 package com.devnp.springboothibernatevalidationdemo.web;import java.util.Date;import org.junit.Before;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.http.MediaType;import org.springframework.test.context.junit4.SpringRunner;import org.springframework.test.web.servlet.MockMvc;import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;import org.springframework.test.web.servlet.result.MockMvcResultMatchers;import org.springframework.test.web.servlet.setup.MockMvcBuilders;import org.springframework.web.context.WebApplicationContext;@RunWith(SpringRunner.class) @SpringBootTest public class StudentControllerTests { @Autowired private WebApplicationContext context; private MockMvc mvc; @Before public void before () { this .mvc = MockMvcBuilders.webAppContextSetup(this .context).build(); } @Test public void testAdd () throws Exception { Date date = new Date (); String enResult = this .mvc .perform(MockMvcRequestBuilders.post("/student/add?lang=en_US" ).contentType(MediaType.APPLICATION_JSON_UTF8) .content("{\"stuName\":\"jack\",\"stuAge\":null, \"stuBirth\":\"" + date.getTime() + "\"}" )) .andExpect(MockMvcResultMatchers.status().isOk()).andReturn().getResponse().getContentAsString(); System.out.println("Result : " + enResult); String zhResult = this .mvc .perform(MockMvcRequestBuilders.post("/student/add?lang=zh_CN" ).contentType(MediaType.APPLICATION_JSON_UTF8) .content("{\"stuName\":\"Tom\",\"stuAge\":12, \"stuBirth\":\"" + date.getTime() + "\"}" )) .andExpect(MockMvcResultMatchers.status().isOk()).andReturn().getResponse().getContentAsString(); System.out.println("结果 : " + zhResult); } }
运行结果:
1 2 3 4 5 6 7 8 9 Student Name Unique Validation Init... Student Age cannot be blank. StudentDto [stuName=jack, stuAge=null, stuBirth=Mon Apr 09 15:24:51 SGT 2018] Result : Success 学生姓名不能重复 学生年龄应该在18到130岁 StudentDto [stuName=Tom, stuAge=12, stuBirth=Mon Apr 09 15:24:51 SGT 2018] 结果 : 请求成功
9.代码下载 spring-boot-hibernate-validation-demo.zip
Author:
Darren Du
License:
Copyright (c) 2019 MIT LICENSE