前端的校验是必须的,这个很简单,因为客户体验。后台的校验更是必须的,关键在于如何与目前我们的分层思想(控制层、业务层、持久层)综合起来考虑。在每层都要进行校验吗?还是只在是某个特定层做就可以了?是否有好的校验框架(如前端的jquery校验框架、springmvc校验框架)?总之校验框架还是有很多的,原理不就是对后端接收的数据进行特定规则的判断,那我们怎么制定规则,有怎么去检验呢?
1、表现层验证:SpringMVC提供对JSR-303的表现层验证; 2、业务逻辑层验证:Spring3.1提供对业务逻辑层的方法验证(当然方法验证可以出现在其他层,但笔者觉得方法验证应该验证业务逻辑); 3、DAO层验证:Hibernate提供DAO层的模型数据的验证(可参考hibernate validator参考文档的7.3. ORM集成)。 4、数据库端的验证:通过数据库约束来进行; 5、客户端验证支持:JSR-303也提供编程式验证支持。
api访问
@PostMapping("/mytest") @ResponseBody public Object myTest(@RequestBody @Valid MyAccount account) { return account; }封装工具
public class ValidationUtil { /** * 开启快速结束模式 failFast (true) */ private static Validator validator = Validation.byProvider(HibernateValidator.class).configure().failFast(false).buildValidatorFactory().getValidator(); /** * 校验对象 * * @param t bean * @param groups 校验组 * @return ValidResult */ public static <T> ValidResult validateBean(T t, Class<?>... groups) { ValidResult result = new ValidationUtil().new ValidResult(); Set<ConstraintViolation<T>> violationSet = validator.validate(t, groups); boolean hasError = violationSet != null && violationSet.size() > 0; result.setHasErrors(hasError); if (hasError) { for (ConstraintViolation<T> violation : violationSet) { result.addError(violation.getPropertyPath().toString(), violation.getMessage()); } } return result; } /** * 校验bean的某一个属性 * * @param obj bean * @param propertyName 属性名称 * @return ValidResult */ public static <T> ValidResult validateProperty(T obj, String propertyName) { ValidResult result = new ValidationUtil().new ValidResult(); Set<ConstraintViolation<T>> violationSet = validator.validateProperty(obj, propertyName); boolean hasError = violationSet != null && violationSet.size() > 0; result.setHasErrors(hasError); if (hasError) { for (ConstraintViolation<T> violation : violationSet) { result.addError(propertyName, violation.getMessage()); } } return result; } /** * 校验结果类 */ @Data public class ValidResult { /** * 是否有错误 */ private boolean hasErrors; /** * 错误信息 */ private List<ErrorMessage> errors; public ValidResult() { this.errors = new ArrayList<>(); } public boolean hasErrors() { return hasErrors; } public void setHasErrors(boolean hasErrors) { this.hasErrors = hasErrors; } /** * 获取所有验证信息 * * @return 集合形式 */ public List<ErrorMessage> getAllErrors() { return errors; } /** * 获取所有验证信息 * * @return 字符串形式 */ public String getErrors() { StringBuilder sb = new StringBuilder(); for (ErrorMessage error : errors) { sb.append(error.getPropertyPath()).append(":").append(error.getMessage()).append(" "); } return sb.toString(); } public void addError(String propertyName, String message) { this.errors.add(new ErrorMessage(propertyName, message)); } /** * 获取去重之后的非法属性值,以逗号分隔 * @return */ public String getProperties() { return errors.stream().map(error -> error.getPropertyPath()).collect(Collectors.toSet()).stream().collect(Collectors.joining(", ")); } } @Data public class ErrorMessage { private String propertyPath; private String message; public ErrorMessage() { } public ErrorMessage(String propertyPath, String message) { this.propertyPath = propertyPath; this.message = message; } } }自定义校验器
@Target({FIELD}) @Retention(RUNTIME) @Documented @Constraint(validatedBy = {DateValidator.DateValidatorInner.class}) public @interface DateValidator { /** * 必须的属性 * 显示 校验信息 * 利用 {} 获取 属性值,参考了官方的message编写方式 * * @see org.hibernate.validator 静态资源包里面 message 编写方式 */ String message() default "日期格式不匹配{dateFormat}"; /** * 必须的属性 * 用于分组校验 */ Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; /** * 非必须 */ String dateFormat() default "yyyy-MM-dd HH:mm:ss"; /** * 必须实现 ConstraintValidator接口 */ class DateValidatorInner implements ConstraintValidator<DateValidator, String> { private String dateFormat; @Override public void initialize(DateValidator constraintAnnotation) { this.dateFormat = constraintAnnotation.dateFormat(); } /** * 校验逻辑的实现 * * @param value 需要校验的 值 * @return 布尔值结果 */ @Override public boolean isValid(String value, ConstraintValidatorContext context) { if (value == null) { return true; } if ("".equals(value)) { return true; } try { Date date = DateUtils.parseDate(value, dateFormat); return date != null; } catch (ParseException e) { return false; } } } }定义分组
public interface AccountGroup { }使用自定义校验器和自定义分组:
@Data @AllArgsConstructor @NoArgsConstructor public class MyAccount { private String id; @NotNull @Length(max = 20) private String userName; @NotNull @Pattern(regexp = "[A-Z][a-z][0-9]") private String passWord; @DateTimeFormat(pattern = "yyy-MM-dd") private Date createTime; // @Pattern(regexp = "[A-Z][a-z][0-9]") @Min(2) private String alias; @Max(10) @Min(1) private Integer level; private Integer vip; @DateValidator(dateFormat = "yyyy-MM-dd", groups = {AccountGroup.class}) private String birthday; }
如果是比较短小的参数直接加@NotNull (int) @NotBlank等就可以了
public int getUser(String username) {
Assert.hasText(username,"用户名不能为空");//用用户名查询用户信息的时候
}
其他断言的用法可以直接看Assert里面的方法,一共没几个,可以校验list等!