之前临时踩坑遇到注解校验的问题,为了避免长篇大论,这里挑了核心的部分介绍如何快速的集成注解校验。
需求:
- 在项目中加入注解校验
- 可以控制错误的操作
- 对于参数错误进行全局日志记录,方便后续查看
ValidataUtils
此工具类配置可以大大减少配置
@Bean public Validator validator() { return new LocalValidatorFactoryBean(); }
下面是具体的工具类,当然不一定需要使用ApplicationContextAware
进行校验替换,也可以直接作为一个validater的工具使用,但是建议使用bean的形式而不是使用静态方法。
@Component public class ValidatorUtils implements ApplicationContextAware { @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { ValidatorUtils.validator = (Validator) applicationContext.getBean("validator"); } private static Validator validator; public static Optional<String> validateResultProcess(Object obj) { Set<ConstraintViolation<Object>> results = validator.validate(obj); if (CollectionUtils.isEmpty(results)) { return Optional.empty(); } StringBuilder sb = new StringBuilder(); for (Iterator<ConstraintViolation<Object>> iterator = results.iterator(); iterator.hasNext(); ) { sb.append(iterator.next().getMessage()); if (iterator.hasNext()) { sb.append(" ,"); } } return Optional.of(sb.toString()); } }
解决思路:
第一种:使用@Valid
加上 BindResult
注意事项:@valid
加入的DTO后面必须跟上 BindResult
,@valid
加入的DTO后面必须跟上 BindResult
,重要的事情说3遍,并且@valid
加入的DTO对象后面必须跟上 BindResult
,如果不按此规则,将会由Spring的 bindException
直接抛出异常
第二种:使用切面的方式统一处理异常
@Aspect @Component public class ControllerValidatorInterceptor { /** * 设置 around 环绕通知切面 */ @Around("execution(* com.aerexu.web.*.*(..)) && args(..,bindingResult)") public Object doAround(ProceedingJoinPoint pjp, BindingResult bindingResult) throws Throwable { Object retVal; if (bindingResult.hasErrors()) { retVal = doErrorHandle(); } else { retVal = pjp.proceed(); } return retVal; } }
第三种:使用validateUtil进行完全的手动校验
下面是对于工具类进行改动之后的代码:
public class ValidatorUtil { /** * 注意使用了快速失败的机制 */ private static Validator validator = Validation.byProvider(HibernateValidator.class) .configure() .failFast(false) .buildValidatorFactory() .getValidator(); public static Validator getValidator() { return validator; } /** * 校验对象 * * @param object * @param groups * @param <T> */ public static <T> void validate(T object, Class<?>... groups) { Set<ConstraintViolation<T>> constraintViolationSet = validator.validate(object, groups); if (!constraintViolationSet.isEmpty()) { throw new ConstraintViolationException(constraintViolationSet); } } // /** // * 校验集合bean内容是否符合校验规则 // * // * @param apiObj 接口传输对象 // * @return // */ // public static ZmtResult validListBean(List<Object> apiObj) { // if (CollectionUtils.isEmpty(apiObj)) { // return ZmtResult.build(401, "请求参数为空"); // } // StringBuilder builder = new StringBuilder(); // for (int i = 0; i < apiObj.size(); i++) { // Object apiPaymentMsgDto = apiObj.get(i); // if (null == apiPaymentMsgDto) { // return ZmtResult.build(400, "第 %s 条信息请求参数为空", i); // } // Validator validator = ValidatorUtil.getValidator(); // Set<ConstraintViolation<Object>> validate = validator.validate(apiPaymentMsgDto); // // 如果不存在校验异常,则返回空信息 // if (org.apache.commons.collections.CollectionUtils.isEmpty(validate)) { // continue; // } // builder.append(String.format("第 %s 条信息:", i + 1)); // for (ConstraintViolation<Object> apiPaymentMsgDtoConstraintViolation : validate) { // builder.append(apiPaymentMsgDtoConstraintViolation.getMessage()); // builder.append(","); // } // builder.deleteCharAt(builder.length() - 1); // builder.append("|"); // } // // 如果存在错误信息,返回错误提示,否则返回空对象证明没有异常 // if (builder.length() > 0) { // builder.deleteCharAt(builder.length() - 1); // return ZmtResult.build(401, builder.toString()); // } else { // return null; // } // // } // /** // * 处理validate异常信息 // * // * @param bindResult 错误绑定对象 // * @return // */ // public static ZmtResult dealWithError(BindingResult bindResult) { // if (bindResult.hasErrors()) { // List<FieldError> fieldErrors = bindResult.getFieldErrors(); // for (FieldError allError : fieldErrors) { // String defaultMessage = allError.getDefaultMessage(); // return ZmtResult.build(400, defaultMessage); // } // } // return null; // } }
实用bindResult 到最佳实践
真的写的十分好,这里分享出来
常用注解
下面列举的都是常用的注解,但是并不是最常用的
- @Null 被注释的元素必须为null
- @NotNull 被注释的元素不能为null
- @AssertTrue 被注释的元素必须为true
- @AssertFalse 被注释的元素必须为false
- @Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
- @Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
- @DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
- @DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
- @Size(max,min) 被注释的元素的大小必须在指定的范围内。
- @Digits(integer,fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
- @Past 被注释的元素必须是一个过去的日期
- @Future 被注释的元素必须是一个将来的日期
- @Pattern(value) 被注释的元素必须符合指定的正则表达式。
- @Email 被注释的元素必须是电子邮件地址
- @Length 被注释的字符串的大小必须在指定的范围内
- @NotEmpty 被注释的字符串必须非空
- @Range 被注释的元素必须在合适的范围内
参考资料
使用Hibernate JSP 303 整合到项目
juejin.im/post/5d3fbe… - 这么写参数校验(validator)就不会被劝退了~