数据校验分为两种:
- 单字段校验(比如:非空、长度、大小等),Java的标准Bean Validation(内部实现是Hibernate Validator)
- 关系多字段校验(比如:时间期间、密码的两次输入等),Spring 的 org.springframework.validation.Validator
(1)单字段校验
Form字段上添加注解
src/main/java/com/rensanning/springboot/web/form/ValidSampleForm.java
public class ValidSampleForm { @NotBlank @Size(max=5) private String name; }
Contrller注入参数前添加@Validated
src/main/java/com/rensanning/springboot/web/ValidSampleContrller.java
@Controller public class ValidSampleContrller { @RequestMapping(value="/validSample", method = RequestMethod.POST) public String postValidSample(@ModelAttribute("form") @Validated ValidSampleForm form, BindingResult result, Model model) { if (result.hasErrors()) { for(FieldError err: result.getFieldErrors()) { log.debug("error code = [" + err.getCode() + "]"); } } return "validSample"; } }
页面中显示错误信息
src/main/resources/templates/validSample.html
一览显示错误信息
(2)自定义错误信息
Spring默认错误信息
引用
hibernate-validator-5.3.4.Final.jar\org\hibernate\validator\ValidationMessages.properties
自定义错误信息
Java中自定义
@NotNull(message="不能为空!")
外部自定义
@NotNull(message="{sample.bean_validation.notNull}")
引用
sample.bean_validation.notNull=不能为空!
SpringBoot自动读取classpath中的ValidationMessages.properties里的错误信息
src/main/resources/ValidationMessages.properties
引用
javax.validation.constraints.Pattern.message=
javax.validation.constraints.Size.message=
javax.validation.constraints.Min.message=
org.hibernate.validator.constraints.NotBlank.message=
javax.validation.constraints.Size.message=
javax.validation.constraints.Min.message=
org.hibernate.validator.constraints.NotBlank.message=
统一到国际化信息文件messages.properties
@Bean public LocalValidatorFactoryBean validator() { LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean(); localValidatorFactoryBean.setValidationMessageSource(messageSource); return localValidatorFactoryBean; } @Override public org.springframework.validation.Validator getValidator() { return validator(); }
(3)嵌套校验 @Valid
public class OrderForm { @NotNull @Valid private AddressForm receiverAddress; @NotNull @Valid private AddressForm senderAddress; } public class AddressForm { @NotNull @Size(min = 1, max = 100) private String address; }
集合Bean校验也和上边一致:
public class OrderForm { @NotNull @Size(min = 1, max = 3) @Valid private Listaddresses; }
(4)组校验
定义Group
public class ValidSampleForm { public static interface Group1 {}; public static interface Group2 {}; @NotBlank(groups=Group1.class) @Size(max=5, groups=Group1.class) private String name; @Min(value=0, groups={Group1.class, Group2.class}) private Integer age; @NotBlank(groups=Group2.class) private Date birthday; }
只校验Group1的字段
@RequestMapping(value="/validSample", method = RequestMethod.POST) public String postValidSample(@ModelAttribute("form") @Validated(Group1.class) ValidSampleForm form, BindingResult result, Model model) { if (result.hasErrors()) { for(FieldError err: result.getFieldErrors()) { log.debug("error code = [" + err.getCode() + "]"); } } return "validSample"; }
顺序执行校验
假如以下定义,如果name为空,两个错误提示都会显示到页面上。
@NotEmpty(message = "请输入姓名!") @Length(min = 1, max = 10, message = "1到10位字符") private String name;
只有@NotEmpty不出错的情况下才执行@Length:
public interface First { } public interface Second { } @GroupSequence({First.class, Second.class}) public interface All { }
@NotEmpty(message = "请输入姓名!", groups = First.class) @Length(min = 1, max = 10, message = "1到10位字符", groups = Second.class) private String name;
@RequestMapping(method = RequestMethod.POST) public String indexPost(@ModelAttribute("form") @Validated(All.class) Person p, BindingResult r) { return "index"; }
(5)多字段校验
实现Spring的Validator
@Component public class ValidSampleValidator implements Validator { public boolean supports(Class> c) { return ValidSampleForm.class.isAssignableFrom(c); } @Override public void validate(Object paramObject, Errors paramErrors) { if (paramErrors.hasFieldErrors("from") || paramErrors.hasFieldErrors("to")) { return; } ValidSampleForm form = (ValidSampleForm)paramObject; Date from = form.getFrom(); Date to = form.getTo(); if (from != null && to != null && from.compareTo(to) > 0) { paramErrors.rejectValue("from", "validator.Period"); } } }
通过@InitBinder添加自定义校验
@Controller public class ValidSampleController { @Autowired private ValidSampleValidator validSampleValidator; @InitBinder public void initBinder(WebDataBinder binder) { binder.addValidators(validSampleValidator); } @RequestMapping(value="/validSample", method = RequestMethod.POST) public String postValidSample(@ModelAttribute("form") ValidSampleForm form, BindingResult result, Model model) { if (result.hasErrors()) { for(FieldError err: result.getFieldErrors()) { log.debug("error code = [" + err.getCode() + "]"); } } return "validSample"; } }
src/main/resources/messages.properties
引用
validator.Period=
一个Controller需要校验多个Form的话:
@Controller public class XxxController { @ModelAttribute("AaaForm") public AaaForm() { return new AaaForm(); } @ModelAttribute("BbbForm") public BbbForm() { return new BbbForm(); } @InitBinder("AaaForm") public void initBinderForAaa(WebDataBinder binder) { binder.addValidators(aaaValidator); } @InitBinder("BbbForm") public void initBinderForBbb(WebDataBinder binder) { binder.addValidators(bbbValidator); } }
(6)自定义校验
a - 已有注解基础上自定义
@NotBlank @Size(min=1, max=5) public @interface Name { String message() default "{com.rensanning.springboot.validator.constraints.Name.message}"; // ... }
引用
com.rensanning.springboot.validator.constraints.Name.message=xxx
@Name private String name;
b - 单字段校验
public class AgeValidator implements ConstraintValidator{ int min; int max; @Override public void initialize(Age annotation) { min = annotation.min(); max = annotation.max(); } @Override public boolean isValid(Integer value, ConstraintValidatorContext paramConstraintValidatorContext) { if (value == null) { return true; } if (value < min || value > max) { return false; } return true; } } @Constraint(validatedBy={AgeValidator.class}) public @interface Age { String message() default "{com.rensanning.springboot.validator.constraints.Age.message}"; int min() default 0; int max() default 100; }
引用
com.rensanning.springboot.validator.constraints.Age.message=
@Age(min=18) private Integer age;
c - 多字段关联校验
@Constraint(validatedBy={PeriodValidator.class}) public @interface Period { String message() default "{com.rensanning.springboot.validator.constraints.Period.message}"; String fieldFrom() default "from"; String fieldTo() default "to"; }
引用
com.rensanning.springboot.validator.constraints.Period.message=
public class PeriodValidator implements ConstraintValidator{ private String fieldFrom; private String fieldTo; private String message; @Override public void initialize(Period annotation) { this.fieldFrom = annotation.fieldFrom(); this.fieldTo = annotation.fieldTo(); this.message = annotation.message(); } @Override public boolean isValid(Object value, ConstraintValidatorContext context) { BeanWrapper beanWrapper = new BeanWrapperImpl(value); Date from = (Date)beanWrapper.getPropertyValue(fieldFrom); Date to = (Date)beanWrapper.getPropertyValue(fieldTo); if (from != null && to != null && from.compareTo(to) > 0) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate(message) .addNode(fieldFrom) .addConstraintViolation(); return false; } return true; } }
@Period(fieldFrom="from", fieldTo="to") public class ValidSampleForm { private Date from; private Date to; }
(7)同时支持Hibernate Validator 和 Spring Validator
@Controller public class UserController { @Autowired private UserFormValidator userFormValidator; @RequestMapping(value = "/save", method = RequestMethod.POST) public String save(@ModelAttribute("userForm") @Valid User user, BindingResult result, Model model) { // 手动执行Spring Validator userFormValidator.validate(user, result); if (result.hasErrors()) { //... } else { //... } } }