Spring Boot 入门 - 基础篇(12)- 数据校验

阅读更多
除过在客户端做JavaScript数据校验外,服务器端做数据校验是很有必要的。Spring提供数据校验,SpringBoot工程里使用没有多大变化。

数据校验分为两种:
  • 单字段校验(比如:非空、长度、大小等),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=


统一到国际化信息文件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 List addresses;

}


(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 {
			//...
		}

	}

}

你可能感兴趣的:(Spring Boot 入门 - 基础篇(12)- 数据校验)