`
rensanning
  • 浏览: 3513828 次
  • 性别: Icon_minigender_1
  • 来自: 大连
博客专栏
Efef1dba-f7dd-3931-8a61-8e1c76c3e39f
使用Titanium Mo...
浏览量:37477
Bbab2146-6e1d-3c50-acd6-c8bae29e307d
Cordova 3.x入门...
浏览量:604288
C08766e7-8a33-3f9b-9155-654af05c3484
常用Java开源Libra...
浏览量:678021
77063fb3-0ee7-3bfa-9c72-2a0234ebf83e
搭建 CentOS 6 服...
浏览量:87242
E40e5e76-1f3b-398e-b6a6-dc9cfbb38156
Spring Boot 入...
浏览量:399799
Abe39461-b089-344f-99fa-cdfbddea0e18
基于Spring Secu...
浏览量:69060
66a41a70-fdf0-3dc9-aa31-19b7e8b24672
MQTT入门
浏览量:90463
社区版块
存档分类
最新评论

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
<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>


(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<AddressForm> 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<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;

}

引用
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<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;

}


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

	}

}
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics