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

chr*_*r0x 6 java validation hibernate

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

notNull.custom = The field {0} can't be null.
Run Code Online (Sandbox Code Playgroud)

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

其中 {0} 应该是传递给验证器的字段名称,如下所示:

@NotNull(field="field name")
Run Code Online (Sandbox Code Playgroud)

我有什么办法可以做到这一点吗?

Bag*_*rma 14

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

constraintContext.disableDefaultConstraintViolation();
constraintContext.buildConstraintViolationWithTemplate(message).addConstraintViolation();
Run Code Online (Sandbox Code Playgroud)

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

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

注释接口 -

public @interface CheckDateIsValid {

    String message() default "default message";

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

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

    String displayPattern();

    String programPattern();
}
Run Code Online (Sandbox Code Playgroud)

注解实现类 -

    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();
    }
}
Run Code Online (Sandbox Code Playgroud)


Ern*_*usc 6

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

ValidationMessages.properties
Run Code Online (Sandbox Code Playgroud)

里面:

javax.validation.constraints.NotNull.message = CUSTOMIZED MESSAGE WHEN NOTNULL is violated!
Run Code Online (Sandbox Code Playgroud)

Hibernate 默认情况下搜索ResourceBundle命名的ValidationMessages. 还可能涉及语言环境:ValidationMessages_en, ValidationMessages_de, <..>

Hibernate将通过参数提供您定制的消息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);
 }
Run Code Online (Sandbox Code Playgroud)

产生的结果:

test.model.exceptions.ConstraintViolationEx
    test.model.Person : name cannot be null
    test.model.Person : surname cannot be null
Run Code Online (Sandbox Code Playgroud)

hibernate提供了有关和ConstraintViolation的相关信息。如您所见,它适用于所有 hibernate 支持的约束,因此您需要检查当前注释是否可以通过 ! 如果可以(由您决定),您应该通过执行“getMessageByAnnotation(<..>)”的约束注释来获取适当的消息。root classrestricted fieldsupportsCustomMessage(<..>)

您所需要做的就是实现不支持的约束逻辑。例如,它可以附加其原因消息或插入默认消息(并且真正的异常将转到*日志文件)