9

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

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 消息参数化是否可行?

请分享您在这件事上的经验。

谢谢 !

4

1 回答 1

34

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

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

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

假设您将 Size 注释的属性消息定义为{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>  

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

于 2011-03-28T08:49:04.633 回答