【问题标题】:Parameterized error messages for javax.validation.constraints.Pattern annotation?javax.validation.constraints.Pattern 注释的参数化错误消息?
【发布时间】:2014-09-03 18:49:47
【问题描述】:

我有一个具有String 类型字段的实体,我在该实体上定义了一个@Pattern 注释,强制该字段的内容与给定的正则表达式匹配。现在我想知道,是否可以参数化约束,例如在定义的验证错误消息中显示第一个不匹配正则表达式的字符。

更糟糕的是,为违反约束而显示的错误消息不是直接在注释中定义,而是在属性文件中定义,如下例所示:

示例类:

public class Address {
  @Pattern(regexp="[a-zA-Z]*", message="paremeterizedMessage")
  private String street;
}

示例属性文件:

parameterizedMessage = Invalid character {0}. Only characters a-z, A-Z allowed.

是否可以用 javax.validation 做这样的事情?我假设 @Pattern 注释无法参数化错误消息。但也许可以使用参数化验证消息定义我自己的ConstraintValidator

【问题讨论】:

    标签: java validation


    【解决方案1】:

    考虑到一些验证注解(例如@Size(min=1,max=5))允许参数化错误消息及其注解参数(例如The String must have a length of {min} to {max} characters.),我找到了一个通常使用占位符(例如{0}、@)的参数化错误消息的解决方案987654324@, ...:

    定义自己的验证约束,例如:

    @Constraint(validatedBy=OnlyCharactersAllowedValidator.class)
    @Documented
    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface OnlyCharactersAllowed {
        String message() default "parameterizedMessage";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
    }
    

    在验证器类中,反射可用于扩展存储所有注释参数的映射(例如minmax 用于@Size)并且可以为键0 添加其他参数, 1等:

    public class OnlyCharactersAllowedValidator implements ConstraintValidator<OnlyCharactersAllowed,String>
    {
        private static final String validCharacters = "abcdefghijklmnopqrstuvwxyz";
        @Override
        public boolean isValid(String text, ConstraintValidatorContext constraintContext)
        {
            for (int index = 0; index < text.length; index++) {
                String aCharacter = text.substring(index, index+1);
                if (validCharacters.indexOf(aCharacter) < 0) {
                    /* Within this message call the magic happens: {0} is mapped to the invalid character. */
                    addMessageParameter(constraintContext, aCharacter);
                    /* Create violation message manually and suppress the default message. */   constraintContext.buildConstraintViolationWithTemplate(constraintContext.getDefaultConstraintMessageTemplate()).addConstraintViolation();
                    constraintContext.disableDefaultConstraintViolation();
                    /* No further validation: show only one error message for invalid characters. */
                    break;
                }
            }
        }
    
        private void addMessageParameter(ConstraintValidatorContext constraintContext, Object... parameter)
        {
            try
            {
                /* Get map for parameters (reflection necessary). */
                Field descriptorField = ConstraintValidatorContextImpl.class.getDeclaredField("constraintDescriptor");
                descriptorField.setAccessible(true);
                @SuppressWarnings("unchecked")
                ConstraintDescriptorImpl<OnlyCharactersAllowed> descriptor = (ConstraintDescriptorImpl<OnlyCharactersAllowed>) descriptorField.get(constraintContext);
    
                Map<String,Object> attributes = descriptor.getAttributes();
    
                /* Copy immutable Map to new Map. */
                Map<String,Object> newAttributes = new HashMap<String,Object>(attributes.size() + parameter.length);
                for (String key : attributes.keySet())
                {
                    newAttributes.put(key, attributes.get(key));
                }
    
                /* Add given parameters to attributes. */
                Integer parameterCounter = 0;
                for (Object param : parameter)
                {
                    newAttributes.put(parameterCounter.toString(), param);
                    parameterCounter++;
                }
    
                /* Set new Map in Descriptor (reflection necessary). */
                Field attributesField = ConstraintDescriptorImpl.class.getDeclaredField("attributes");
                attributesField.setAccessible(true);
                attributesField.set(descriptor, newAttributes);
            }
            catch (NoSuchFieldException | IllegalAccessException | SecurityException | ClassCastException e1)
            {
                /* Do nothing in case of exception. Unparameterized message is shown. */
            }
        }
    }
    

    即使您的验证消息是在诸如属性文件中定义的,此解决方案也可以工作。

    parameterizedMessage = Invalid character: Do not use the {0} character.
    

    【讨论】:

    • 嗨 Claas,validCharacters 没有被使用并且 text.length 没有被解析。我不明白你为什么要一个一个地解析一个字符串。
    • 嗨斯蒂芬,谢谢你的评论。当我试图匿名化某些公司的生产代码时,示例中有一个错误。现在,应该更明智了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-08
    • 1970-01-01
    • 2018-11-01
    相关资源
    最近更新 更多