Bean Validation在JSF 2中的resourcebundle参数化可能性?

时间:2011-03-21 16:44:18

标签: java jsf jsf-2 resourcebundle bean-validation

在JSF 2中使用带有BV的resourcebundle看起来像这样:

public class UserBean {
    @Size(min=13, message="{creditcard.length}")
    public String getCreditCard() {
        return this.creditCard;
    }
}

我必须在其中一个属性文件中定义ResourceBundle条目,该文件可以在faces-config.xml

中注册
  

creditcard.length =信用卡长度必须至少为13个字符

我们可以看到creditcard.length的值是非参数化的。

我可以进行可以从BV或其他地方填充的参数化ResourceBundle条目吗?


这是一个我想要实现的简单场景:

creditcard.length =信用卡长度必须至少为{0}个字符。感谢您选择{1}信用卡。

我希望有这样的事情:

public class UserBean {
    @Size(
        min=13, 
        message="{creditcard.length}", 
        messageParams={"13", "plantvszombie"})
    public String getCreditCard() {
        return this.creditCard;
    }
}

当验证失败时,creditcard属性的错误消息将显示如下字符串:

信用卡长度必须至少为 13 个字符。感谢您选择 plantvszombie 信用卡。


这个ResourceBundle消息参数化是否可行?

请分享您对此事的经验。

谢谢!

1 个答案:

答案 0 :(得分:32)

也许您已经知道Bean验证的消息是在类的根目录中的资源包ValidationMessages.properties中定义的(即WEB-INF\classes\ValidationMessages.properties)。

这些消息可以包含参数,但它们不像JSF那样工作。有一个名为MessageInterpolator的接口,它将消息模式转换为实际消息。

默认插值器适用于消息中的命名参数:值必须介于{min}和{max} 之间。 {} 之间的值首先在应用程序的资源包中解析;稍后在提供程序的资源包中,最后在约束注释的属性中。 (这或多或少是如何工作的,完整的算法在Bean Validation规范的4.3节中。)

假设您将尺寸注释的属性消息定义为 {creditCard.message}

ValidationMessage.properties的内容可能是

creditCard.message=Credit card length must be at least {min} characters. \
                   Thank you for choosing the plantsvszombies credit card.

您可以使用属性替换plantsvszombies:

creditCard.message=Credit card length must be at least {min} characters. \
                   Thank you for choosing the {creditCard.type} credit card.
creditCard.type=plantsvszombies

你甚至可以在约束的消息中使用两个参数

Size(min=13, message="{creditCard.message} {plantsvszombies.messages}")

并将资源包定义为

creditCard.message=Credit card length must be at least {min} characters.
plantsvszombies.message=Thank you for choosing the plantsvszombies credit card.

我认为这是一种简单而干净的方法。


但是如果你想要更多的东西,比如在约束声明中定义自定义参数,你可以使用自定义消息插补器。 请注意,这可能是一个棘手的解决方案

好吧,你可以定义一个语法来在消息字符串中引入你的参数。然后,让默认插值器解析消息。默认插补器不会理解自定义参数的语法,它们在解析后仍然存在。然后,自定义插补器可以替换替换自定义参数。

通过示例更容易理解。

首先,消息定义为 {creditCard.message} [plantsvszombies] 。 对于此语法,方括号之间的内容是以逗号分隔的索引参数(此处只有一个参数)。

接下来,资源包的内容定义为:

 creditCard.message=Credit card length must be at least {min} characters. \
                    Thank you for choosing the {0} credit card.

当默认插值器替换表达式的第一部分时,我们将:

信用卡长度必须至少为13个字符。 \感谢您选择{0}信用卡。[plantsvszombies]

然后,自定义插值器将采用最后一个表达式并拆分内容以获取标记,并使用相应索引中的标记替换索引参数(参数[0] = plantsvzzombies)。

所以消息将是:

信用卡长度必须至少为13个字符。 \感谢您选择plantsvszombies信用卡。

这是此语法的自定义插值器的代码(未优化,如果第一个表达式或标记中有其他方括号,则正则表达式模式无效。)

 package validation;

 import java.util.Locale;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import javax.validation.MessageInterpolator;
 import javax.validation.Validation;

 public class MyInterpolator implements MessageInterpolator{
    private MessageInterpolator interpolator;

    public MyInterpolator() {
        //we need to delegate to the default interpolator
        this.interpolator = Validation.byDefaultProvider().configure().getDefaultMessageInterpolator();
    }

    private static final Pattern parametersPattern=Pattern.compile("\\[(.+)\\]$");

    protected static String replaceParameters(String message){
        Matcher matcher = parametersPattern.matcher(message);
        String values[]={};
        if(matcher.find()){
            values=matcher.group(1).split("\\s*,\\s*");
            message=message.substring(0, matcher.start());
            for(int i=0; i < values.length; i++){
                message=message.replace("{"+i+"}", values[i]);
            }
        }
        return message;
    }

    @Override
    public String interpolate(String messageTemplate, Context context) {
        String message = interpolator.interpolate(messageTemplate, context);
        return replaceParameters(message);
    }

    @Override
    public String interpolate(String messageTemplate, Context context, Locale locale) {
        String message = interpolator.interpolate(messageTemplate, context);
        return replaceParameters(message);
    }

}

内插器的注册位于名为META-INF / validation.xml的xml文件中(规范的4.4.6)。

<?xml version="1.0" encoding="UTF-8"?>
<validation-config
xmlns="http://jboss.org/xml/ns/javax/validation/configuration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://jboss.org/xml/ns/javax/validation/configuration validation-configuration-1.0.xsd">
    <message-interpolator>validation.MyInterpolator</message-interpolator>
</validation-config>  

这是一个有点复杂的解决方案,因为约束注释不接受消息的参数,因为在插值器中,我们无法获得正在验证的属性的许多信息。如果我找到一个更简单的解决方案,我会发布它。