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

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

数据校验分为两种:
[list][*]单字段校验(比如:非空、长度、大小等),Java的标准Bean Validation(内部实现是Hibernate Validator)
[*]关系多字段校验(比如:时间期间、密码的两次输入等),Spring 的 org.springframework.validation.Validator[/list]

[b](1)单字段校验[/b]

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
<form th:action="@{/validSample}" th:object="${form}" method="post">
<table>
<tr>
<td>姓名:</td>
<td>
<input type="text" th:field="*{name}" th:errorclass="fieldError" />
</td>
<td th:if="${#fields.hasErrors('name')}" th:errors="*{name}" style="color: red"></td>
</tr>
</table>
</form>


一览显示错误信息
<ul>
<li th:each="e : ${#fields.detailedErrors()}"
th:class="${e.global}? globalerrMsg : fielderrMsg" th:text="${e.message}" />
</ul>


[b](2)自定义错误信息[/b]

Spring默认错误信息
[quote]hibernate-validator-5.3.4.Final.jar\org\hibernate\validator\ValidationMessages.properties[/quote]

自定义错误信息
Java中自定义
@NotNull(message="不能为空!")

外部自定义
@NotNull(message="{sample.bean_validation.notNull}")

[quote]sample.bean_validation.notNull=不能为空![/quote]

SpringBoot自动读取classpath中的ValidationMessages.properties里的错误信息
src/main/resources/ValidationMessages.properties
[quote]javax.validation.constraints.Pattern.message=
javax.validation.constraints.Size.message=
javax.validation.constraints.Min.message=
org.hibernate.validator.constraints.NotBlank.message=[/quote]

统一到国际化信息文件messages.properties
@Bean
public LocalValidatorFactoryBean validator() {
LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
localValidatorFactoryBean.setValidationMessageSource(messageSource);
return localValidatorFactoryBean;
}

@Override
public org.springframework.validation.Validator getValidator() {
return validator();
}


[b](3)嵌套校验 @Valid[/b]
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<AddressForm> addresses;

}


[b](4)组校验[/b]

定义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";
}


[b]顺序执行校验[/b]

假如以下定义,如果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";
}


[b](5)多字段校验[/b]

实现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
[quote]validator.Period=[/quote]

一个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);
}

}


[b](6)自定义校验[/b]

[b]a - 已有注解基础上自定义[/b]
@NotBlank
@Size(min=1, max=5)
public @interface Name {
String message() default "{com.rensanning.springboot.validator.constraints.Name.message}";
// ...
}

[quote]com.rensanning.springboot.validator.constraints.Name.message=xxx[/quote]
@Name
private String name;


[b]b - 单字段校验[/b]
public class AgeValidator implements ConstraintValidator<Age, Integer> {
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;

}

[quote]com.rensanning.springboot.validator.constraints.Age.message=[/quote]
@Age(min=18)
private Integer age;


[b]c - 多字段关联校验[/b]
@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";

}

[quote]com.rensanning.springboot.validator.constraints.Period.message=[/quote]
public class PeriodValidator implements ConstraintValidator<Period, Object> {
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;

}


[b](7)同时支持Hibernate Validator 和 Spring Validator[/b]
@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 {
//...
}

}

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值