繁体   English   中英

JSF 2 - Bean 验证:验证失败 -> 空值替换为来自托管 bean 的最后一个有效值

[英]JSF 2 - Bean Validation: validation failed -> empty values are replaced with last valid values from managed bean


我不明白 JSF2 在验证期间的行为。 希望有人可以帮助我。

我有一个表单,在(ajax)提交后验证字段 - 好的
如果验证失败,则会显示一条错误消息 - 好的

对于我的示例,当我输入有效的生日并且字段名称为空时,提交后会显示名称的错误消息。
现在,当我输入有效姓名并从生日字段中删除输入时,会显示生日错误消息(没关系)但现在旧的“有效”生日也在输入字段中!?!

我怎样才能避免这种行为? 当我提交一个空字段时,我想看到一条错误消息和一个空字段...

这是我的示例代码:

我使用包含 EntityBean ( Contact ) 的 ManagedBean ( TestBean )。 联系人包含每个注释的验证。

public class Contact implements Serializable {
    @NotNull 
    @Temporal(TemporalType.DATE)
    private Date birthday;

    @NotNull 
    @Size(min=3, max=15)
    private String name;

    //...
}

我的ManagedBean

@ManagedBean
@ViewScoped
public class TestBean implements Serializable {
    private Contact contact;

    @PostConstruct
    void init() {
        System.out.println("init...");
        contact = new Contact(); 
    }

    public void newContact(ActionEvent ae) {
        System.out.println("newContact...");
        contact = new Contact();
    }

    public void save() {
        System.out.println("save...");
        //TODO do something with contact...
    }

    public Contact getContact() { return contact; }

    public void setContact(Contact contact) {this.contact = contact;}
}

这里是我的 JSF 页面:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core" >      
 <h:body>
    <h:form>     
        <h:panelGrid columns="3">   

        <h:outputText value="Birthday: " />  
        <h:inputText id="birthday" value="#{testBean.contact.birthday}">
            <f:convertDateTime/>
        </h:inputText>  
        <h:message for="birthday" />  

        <h:outputText value="Name: " />  
        <h:inputText id="name" value="#{testBean.contact.name}"/>
        <h:message for="name" />

        </h:panelGrid>  

        <h:commandButton value="submit" action="#{testBean.save}"> 
            <f:ajax execute="@form" render="@form"/>
        </h:commandButton> 

        <h:commandButton value="newContact" actionListener="#{testBean.newContact}"
                         immediate="true"> 
            <f:ajax render="@form"/>
        </h:commandButton> 

    </h:form>
</h:body>
</html>

最后一个来自 web.xml 的片段

<context-param>
    <param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
    <param-value>true</param-value>
</context-param>

<context-param>
    <param-name>javax.faces.VALIDATE_EMPTY_FIELDS</param-name>
    <param-value>true</param-value>
</context-param>

谢谢你的一些提示

您的特定问题是由

<context-param>
    <param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
    <param-value>true</param-value>
</context-param>

以及HtmlBasicRenderer#getCurrentValue()的一个错误(至少是一个疏忽):

if (component instanceof UIInput) {
    Object submittedValue = ((UIInput) component).getSubmittedValue();
    if (submittedValue != null) {
        // value may not be a String...
        return submittedValue.toString();
    }
}

String currentValue = null;
Object currentObj = getValue(component);
if (currentObj != null) {
    currentValue = getFormattedValue(context, component, currentObj);
}
return currentValue;

通常,当UIInput组件成功转换和验证时,提交的值设置为null 当 JSF 将要重新显示该值时,它首先检查提交的值是否为null然后再继续重新显示模型值。 但是,对于这个上下文参数,当它无效时它是null而不是空字符串,因此当您删除必填字段的初始值时,它将始终重新显示原始模型值。

要测试它,请将该上下文参数值设置为false或将其完全删除。 你会看到它按预期工作。 但是,它会带来一个缺点,即您的模型值将在空但非必需的字段上被空字符串弄乱,并且您将失去使用 JSR 303 bean 验证的@NotNull注释的优势。

要解决此问题,您必须按如下方式更改HtmlBasicRenderer#getCurrentValue()的第一部分:

if (component instanceof UIInput && !((UIInput) component).isValid()) {
    Object submittedValue = ((UIInput) component).getSubmittedValue();
    if (submittedValue != null) {
        // value may not be a String...
        return submittedValue.toString();
    } else {
        return null;
    }
}

我已经将其作为issue 2266报告给了 Mojarra 人员。

有一个类似的问题,当字段被清空并且另一个组件验证失败时,从支持 bean 加载的值将被重置。 我必须对 BalusC 的代码稍加添加才能使其工作。

protected String getCurrentValue(FacesContext context,
                                     UIComponent component) {

        if (component instanceof UIInput && !((UIInput) component).isValid()) {
            Object submittedValue = ((UIInput) component).getSubmittedValue();
            if (submittedValue != null) {
                // value may not be a String...
                return submittedValue.toString();
            } else {
                return null;
            }
        }


        String currentValue = null;
        Object currentObj;

        if ( component instanceof UIInput && ((UIInput)component).isLocalValueSet() )
        {
           currentObj = ((UIInput)component).getLocalValue();
        }
        else {
            currentObj = getValue(component);
        }

        if (currentObj != null) {
            currentValue = getFormattedValue(context, component, currentObj);
        }
        return currentValue;


    }

我认为在正常使用期间人们不会输入有效日期,提交,然后在再次提交之前删除日期。 我意识到您在测试期间发现了这一点,但可能人们只是想成功填写表单而不是删除他们已经输入的内容,在这种情况下,保留最后一个有效值是最好的功能。

如果你坚持......似乎setter方法“生日”从未被调用,因为该值无效,然后当页面重新显示时,“生日”的当前值被显示(当前值是有效值)以前保存)。 也许您可以编写一个自定义验证器来设置值然后验证该值,但这并没有多大意义。 对于用户输入诸如“昨天”之类的文本字符串而不是有效日期的情况,您仍然必须首先验证该值,然后您必须根据该无效值将日期设置为某个值,然后您将有将消息添加到 FacesContext。 因此,代码必须执行以下操作。

1) 验证日期值
2) 如果它无效,则将该字段设置为某个有意义的值并向 FacesContext 添加错误消息。
3) 如果它有效,则使用它。

这是可行的,但很奇怪,因为即使传入的值无效,您也在更改字段的值...

Aaron 已经描述了这种行为。

通过单击“newContact”按钮,我所描述的问题也存在。 如果第一次提交无效(输入生日,名称字段为空),则会显示错误消息。 好的。

之后“newContact”按钮不会刷新(清除)视图。 尽管模型已重置(contact = new Contact())。

我在这里找到了一些提示: http ://wiki.apache.org/myfaces/ClearInputComponents

这是我的解决方案:

public void newContact(ActionEvent ae) {
    contact = new Contact();
    contact.setBirthday(new Date()); //for testing only

    resetForm(ae.getComponent());
}

private void resetForm(UIComponent uiComponent) {
    //get form component
    UIComponent parentComponent = uiComponent.getParent();
    if (uiComponent instanceof UIForm)
        resetFields(uiComponent);
    else if (parentComponent != null)
        resetForm(parentComponent);
    else
        resetFields(uiComponent);

}

private void resetFields(UIComponent baseComponent) {
    for (UIComponent c : baseComponent.getChildren()) {
        if (c.getChildCount() > 0)
            resetFields(c);

        if (c instanceof UIInput)
            ((UIInput) c).resetValue();
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM