为什么我的bean验证消息没有被提取?

时间:2012-02-28 14:53:32

标签: java jsf bean-validation hibernate-validator

我正在为以后的i18n定义一些bean验证消息。 此消息应验证输入的类型为number / BigDecimal:

class Payment {
    @Digits(message = "test error msg", fraction = 2, integer = 13)
    private BigDecimal amount;
}

<p:inputText value="#{payment.amount}" />

不幸的是,如果我尝试输入文本,我不会得到我定义的消息,但是:

'asd' must be a signed decimal number

有人可以解释原因吗?或者更确切地说,我如何强制我的输入字段始终使用@Digits进行验证?

3 个答案:

答案 0 :(得分:5)

在JSF验证中,转换后发生。如果你输入的东西不是在映射到支持bean中的BigDecimal的文本字段中的数字,JSF将尝试将其转换为BigDecimal,当然它将失败。所以Hibernate Validator实际上没有机会参与进来。你需要本地化JSF转换消息来实现你想要的。

我不熟悉@Digits注释。但是Hibernate Validator docs似乎表明它验证了数字是否正在使用预期的整数和小数位数。因此,不要在文本字段中输入文本,而是尝试输入验证器接受的格式不同的值(在您的情况下,分数= 2,整数= 13)。因此,请尝试输入值123.123120,看看是否收到了预期的消息。

这是黑客一直进入实体并获得转换器的注释。首先,您需要为数字创建自定义转换器(在本例中为BigDecimal):

@FacesConverter("MyBigDecimalConverter")
public class MyBigDecimalConverter extends NumberConverter {
@Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) {
    try {
        HtmlInputText it = (HtmlInputText) arg1;
        ValueExpression ve = it.getValueExpression("value");
        String expression = ve.getExpressionString();
        String field = expression.replaceAll("#\\{.*\\.", "");
        field = field.replace("}", "");
        String parent = expression.replace("." + field, "");
        ExpressionFactory expressionFactory = arg0.getApplication().getExpressionFactory();
        ValueExpression exp = expressionFactory.createValueExpression(arg0.getELContext(), parent, Object.class);
        Object obj = exp.getValue(arg0.getELContext());

        Digits d = null;    
        Field f = obj.getClass().getDeclaredField(field);
        d = f.getAnnotation(Digits.class);
        return super.getAsObject(arg0, arg1, arg2);
    } catch (ConverterException e) {
        FacesMessage msg = new FacesMessage(d.message());
        msg.setSeverity(FacesMessage.SEVERITY_ERROR);
        throw new ConverterException(msg);
    } catch (Exception e) {
        return super.getAsObject(arg0, arg1, arg2);
    }
}

@Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2) {
    return super.getAsString(arg0, arg1, arg2);
}

}

您需要在页面中使用该转换器:

    <h:outputText value="#{someBean.someEntity.nome}" />
    <h:form>
        <p:inputText value="#{someBean.someEntity.someBigDecimal}" id="test">
            <f:converter converterId="MyBigDecimalConverter" />
        </p:inputText>
        <h:message for="test"></h:message>
        <p:commandButton value="teste" process="@form" update="@form"></p:commandButton>
    </h:form>

我在这里使用primeFaces组件,但你真的不需要它们。

转换器到底在做什么?它正在获取bean中的对象,该对象包含inputText引用的值和字段名称。然后,它找到该字段的@Digits注释并读取其消息。它要求NumericConverter将String转换为数字。如果失败,它会使用@Digits注释中的消息来显示错误消息。

答案 1 :(得分:0)

您应该使用<f:validateBean />将验证委托给Bean Validation API(JSR 303)

<p:inputText value="#{payment.amount}" >
    <f:validateBean />
</p:inputText>

答案 2 :(得分:0)

不确定这个<p:inputText>标签,但在vanilla JSF中你可以:

<h:inputText id="price"
    value="#{movieController.movie.price}" redisplay="true"
    converterMessage="Please input a decimal">
    <f:ajax event="blur" render="numberOfCopiesMessage" />
</h:inputText>

注意converterMessage属性。

虽然这可能看起来像是违反DRY(因为你已经在实体中有了消息),但事实并非如此。您的模型与提交'asd'整数的用户无关 - 它实际上是视图事项