如何基于注释属性创建自定义验证消息?

时间:2015-04-14 17:19:26

标签: java hibernate validation

我正在使用Hibernate @NotNull验证器,我正在尝试创建自定义消息,告诉用户哪个字段在为null时生成了错误。像这样:

notNull.custom = The field {0} can't be null.

(这将在我的ValidationMessages.properties文件中)。

{0}应该是以这种方式传递给验证器的字段名称:

@NotNull(field="field name")

我能做到这一点吗?

2 个答案:

答案 0 :(得分:2)

如果可以通过插入hibernate消息来满足您的要求,那么您可以像这样创建/命名您的*属性文件:

ValidationMessages.properties

在里面:

javax.validation.constraints.NotNull.message = CUSTOMIZED MESSAGE WHEN NOTNULL is violated!

默认情况下,Hibernate会搜索名为ResourceBundle的{​​{1}}。还可能涉及区域设置:ValidationMessagesValidationMessages_en,< ..>

Hibernate将通过ValidationMessages_de参数提供您的自定义消息,因此将显示所有interpolatedMessage相关信息(包括您的消息)。所以你的消息将成为真正异常的一部分。将提供一些笨拙的信息!

如果您想进行自定义异常(没有默认的ConstraintViolationException行为),请查看以下内容:

使用ConstraintViolationException概念,请考虑以下内容

GenericDao

制作结果:

  public void saveOrUpdate(IEntity<?> entity) {
      try {
          if(entity.getId == null) {
            em.persist(entity);
          } else {
            em.merge(entity)l
          }
      } catch(ConstraintViolationException cve) {
          throw new ConstraintViolationEx(constructViolationMessage(cve.getConstraintViolations()));
      }
  }

 private String constructMessage(Set<ConstraintViolation<?>> pConstraintViolations) {
    StringBuilder customMessages = new StringBuilder();
    for(ConstraintViolation<?> violation : pConstraintViolations) {
        String targetAnnotation = violation.getConstraintDescriptor().getAnnotation().annotationType().getSimpleName();
        if(supportsCustomMessage(targetAnnotation)) {
            applyMessage(violation, targetAnnotation, customMessages);
        } else {
            // do something with not customized constraints' messages e.g. append it to existing container
        }
    }
    return customMessages.toString();
 }

 private void applyMessage(ConstraintViolation<?> pViolation, String pTargetAnnotation, StringBuilder pCustomMessages) {
     String targetClass = pViolation.getRootBean().getClass().getName();
     String targetField = pViolation.getPropertyPath().toString();
     pCustomMessages.append(MessageFormat.format(getMessageByAnnotation(pTargetAnnotation), targetClass, targetField));
     pCustomMessages.append(System.getProperty("line.separator"));
 }


 private String getBundleKey() {
     return "ValidationMessages"; //FIXME: hardcoded - implement your true key
 }

 private String getMessageByAnnotation(String pTargetAnnotation) {
     ResourceBundle messages = ResourceBundle.getBundle(getBundleKey());
     switch(pTargetAnnotation) {
     case "NotNull":
         return messages.getString(pTargetAnnotation + ".message");
     default:
         return "";
     }
 }

 private boolean supportsCustomMessage(String pTargetAnnotation) {
     return customizedConstraintsTypes.contains(pTargetAnnotation);
 }

休眠test.model.exceptions.ConstraintViolationEx test.model.Person : name cannot be null test.model.Person : surname cannot be null 提供有关ConstraintViolationroot class的相关信息。如您所见,它适用于所有hibernate支持的约束,因此您需要检查restricted field是否可以自定义当前注释!如果它可以(它取决于你),你应该通过约束注释得到适当的消息`getMessageByAnnotation(&lt; ..&gt;)&#39;。

您需要做的就是实现不受支持的约束逻辑。例如,它可以附加它的原因消息或使用默认消息进行插值(真正的异常转到*日志文件)

答案 1 :(得分:1)

要自定义注释消息,您需要禁用isValid()方法中现有的违规消息,并构建新的违规消息并将其添加。

constraintContext.disableDefaultConstraintViolation();
constraintContext.buildConstraintViolationWithTemplate(message).addConstraintViolation();

在下面给出的示例中,我将基于“无效日期”,“不能大于今天的日期”和“日期格式正确与否”创建用于输入日期验证的注释。

@CheckDateIsValid(displayPattern = "DD/MM/YYYY", programPattern = "dd/MM/yyyy", groups = Order2.class)
    private String fromDate;

注释界面-

public @interface CheckDateIsValid {

    String message() default "default message";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    String displayPattern();

    String programPattern();
}

注释实现类-

    public class CheckDateIsValidValidator implements ConstraintValidator<CheckDateIsValid, String> {
    @Value("${app.country.timeZone}")
    private String timeZone;
    private String displayPattern;
    private String programPattern;

    @Override
    public void initialize(CheckDateIsValid constraintAnnotation) {
        this.displayPattern = constraintAnnotation.displayPattern();
        this.programPattern = constraintAnnotation.programPattern();
    }

    @Override
    public boolean isValid(String object, ConstraintValidatorContext constraintContext) {
        try {
            // disable existing violation message
            constraintContext.disableDefaultConstraintViolation();

            if (object == null) {
                return true;
            }

            final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(programPattern);
            LocalDateTime time = LocalDate.parse(object, formatter).atStartOfDay();

            ZoneOffset zoneOffSet = ZoneOffset.of(timeZone);
            OffsetDateTime todayDateTime = OffsetDateTime.now(zoneOffSet);

            if (time == null) {
                customMessageForValidation(constraintContext, "date is not valid");
                return false;
            } else if (todayDateTime.isBefore(time.atOffset(zoneOffSet))) {
                customMessageForValidation(constraintContext, "can't be greater than today date");
                return false;
            }

            return time != null;
        } catch (Exception e) {
            customMessageForValidation(constraintContext, "date format should be like " + displayPattern);
            return false;
        }
    }

    private void customMessageForValidation(ConstraintValidatorContext constraintContext, String message) {
        // build new violation message and add it
        constraintContext.buildConstraintViolationWithTemplate(message).addConstraintViolation();
    }
}