Java JSF2-Bean验证:验证失败->;空值将替换为托管bean中的最后一个有效值
Java JSF2-Bean验证:验证失败->;空值将替换为托管bean中的最后一个有效值,java,validation,jsf-2,Java,Validation,Jsf 2,我不理解JSF2在valdation期间的行为。希望有人能帮助我 我有一个表单,其中字段在(ajax)提交后进行验证-确定 如果验证失败,将显示一条错误消息-确定 例如,当我输入一个有效的生日,并且字段名为空时,提交后会显示一条关于姓名的错误消息。 现在,当我输入一个有效的名字并从生日字段中删除输入时,会显示一条关于生日的错误消息(没关系),但现在旧的“有效”生日也出现在输入字段中 我怎样才能避免这种行为? 当我提交一个空字段时,我希望看到一条错误消息和一个空字段 以下是我的示例代码: 我使用一
我不理解JSF2在valdation期间的行为。希望有人能帮助我 我有一个表单,其中字段在(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:
下面是我的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>
javax.faces.explait_EMPTY_STRING_SUBMITTED_VALUES_为_NULL
真的
javax.faces.VALIDATE_空_字段
真的
感谢您提供的一些提示我认为在正常使用期间,人们不会输入有效的日期、提交,然后在再次提交之前删除该日期。我知道您在测试期间发现了这一点,但可能人们只是试图成功地填写表单,而不是删除他们已经输入的内容,在这种情况下,保留最后一个有效值可能是最好的功能 如果你坚持。。。似乎从未调用setter方法“生日”,因为该值无效,然后当重新显示页面时,将显示“生日”的当前值(当前值是以前保存的有效值)。也许您可以编写一个自定义验证器来设置值,然后验证值,但这并没有多大意义。对于用户输入“昨天”之类的文本字符串而不是有效日期的情况,您仍然必须首先验证该值,然后必须根据该无效值将日期设置为某个值,然后必须将消息添加到FacesContext。因此,代码必须执行以下操作。
1) 验证日期值
2) 如果无效,则将字段设置为有意义的值,并向FacesContext添加错误消息。
3) 如果它是有效的,那么就使用它
这是可以做到的,但很奇怪,因为您正在更改字段的值,即使传入的值无效…Aaron已经描述了该行为 通过单击“新建联系人”按钮,我描述的问题也存在。如果第一次提交无效(输入生日,名称字段为空),将显示错误消息。嗯 之后,“newContact”按钮不会刷新(清除)视图。尽管模型已重置(contact=newcontact()) 我在这里找到了一些提示: 以下是我的解决方案:
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();
}
}
你的特殊问题是由
javax.faces.explait_EMPTY_STRING_SUBMITTED_VALUES_为_NULL
真的
以及Mojarra的HtmlBasicRenderer#getCurrentValue()
中的一个bug(至少是一个疏忽):
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
,或将其全部删除。您将看到它按预期工作。但是,它会带来一个缺点,即您的模型值将在空但非必需的字段上被空字符串弄乱,并且您将失去使用JSR303bean验证的@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;
}
}
我已经向Mojarra guys报告过了。有一个类似的问题,当字段被清空并且另一个组件验证失败时,从支持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;
}
嗨,亚伦,谢谢你的回答!在我的示例代码中,还有一个案例在尝试替换后端模型(button newContact)时显示了相同的问题。对我来说,这看起来像是一个现实世界的问题,而不是一个学术测试案例。我找到了一个解决方案(不太好,但很有效…)。对于解决方案,我稍微编辑了示例代码。查看我自己的答案我注意到,当输入通过转换器绑定到非字符串属性时,可能由于相同的根本原因,会出现类似的行为。当您输入一个空字符串并且验证由于不同的字段而失败时,您的(有效)空字符串将被bean中的值覆盖。被报道为@BalusC:谢谢你的详细解释!你在这里的解决方案/解释常常让我感到很高兴;-)@BalusC这是否意味着他编译了JSF的自定义版本?我也遇到过同样的问题,希望这不是唯一的解决办法。@Mike:对于初学者来说,最简单的方法是将
HtmlBasicRenderer
的原始.java
源文件直接复制粘贴到你的webapp项目中(同时保留原始包!),然后编辑它。它将在类加载中优先于
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;
}