java JSF 2 - Bean 验证:验证失败 -> 空值替换为来自托管 bean 的最后一个有效值
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3933786/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
JSF 2 - Bean Validation: validation failed -> empty values are replaced with last valid values from managed bean
提问by Matthias Reining
I do not understand the behaviour of JSF2 during valdation. Hope someone can help me.
我不明白 JSF2 在验证期间的行为。希望可以有人帮帮我。
I have a form where the fields are validated after (ajax) submit - ok
If the validation failed a error message is shown - ok
我有一个表单,在(ajax)提交后验证字段 - ok
如果验证失败,则会显示一条错误消息 - ok
For my example when I enter a valid birthdayand the field nameis empty an errormessage for nameis shown after submit.
Now when I enter a valid nameand delete the input from the birthdayfield an errormessage is show for birthday(that's ok) but now the old 'valid' birthdaystands also in the input field!?!
对于我的示例,当我输入有效的生日并且字段名称为空时,提交后会显示名称的错误消息。
现在,当我输入有效姓名并从生日字段中删除输入时,会显示生日错误消息(没关系)但现在旧的“有效”生日也在输入字段中!?!
How can I avoid this behaviour? When I submit an empty field I want to see an errormessage and an empty field...
我怎样才能避免这种行为?当我提交一个空字段时,我想看到一条错误消息和一个空字段...
Here's my sample code:
这是我的示例代码:
I use a ManagedBean (TestBean) that contains an EntityBean (Contact). The Contactcontains validations per annoations.
我使用包含 EntityBean ( Contact)的 ManagedBean ( TestBean)。该联系人包含每个annoations验证。
public class Contact implements Serializable {
@NotNull
@Temporal(TemporalType.DATE)
private Date birthday;
@NotNull
@Size(min=3, max=15)
private String name;
//...
}
My ManagedBean:
我的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;}
}
An here my JSF page:
这里是我的 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>
at last a snippet from web.xml
最后一个来自 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>
Thanks for some tips
谢谢你的一些提示
采纳答案by BalusC
Your particular problem is caused by
您的特定问题是由
<context-param>
<param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
<param-value>true</param-value>
</context-param>
and a bug (at least, an oversight) in HtmlBasicRenderer#getCurrentValue()
of Mojarra:
以及HtmlBasicRenderer#getCurrentValue()
Mojarra 的一个错误(至少是一个疏忽):
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;
Normally, the submitted value is set to null
when the UIInput
component is successfully converted and validated. When JSF is about to redisplay the value, it first checks if the submitted value is not null
before proceeding to redisplay the model value. However, with this context parameter, it is null
instead of an empty string when it is invalid and thus it will always redisplay the original model value when you remove the initial value of a required field.
通常,提交的值设置为null
当UIInput
组件成功转换和验证时。当 JSF 将要重新显示该值时,它首先检查提交的值是否不是,null
然后再继续重新显示模型值。但是,使用此上下文参数,null
当它无效时它会代替空字符串,因此当您删除必填字段的初始值时,它将始终重新显示原始模型值。
To test it, set that context param value to false
or remove it altogether. You'll see that it works as intended. However, it will bring back the disadvantage that your model values will be cluttered with empty strings on empty but non-required fields and you'll lose the advantage of using @NotNull
annotation of JSR 303 bean validation.
要测试它,请将该上下文参数值设置为false
或完全删除它。您会看到它按预期工作。但是,它会带来一个缺点,即您的模型值将在空但非必需的字段上被空字符串弄乱,并且您将失去使用@NotNull
JSR 303 bean 验证注释的优势。
To fix this, you've to alter the first part of HtmlBasicRenderer#getCurrentValue()
as follows:
要解决此问题,您必须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;
}
}
I've already reported it to Mojarra guys as issue 2262.
我已经将它作为issue 2262报告给了 Mojarra 的人。
回答by andyfinch
Had a similar issue where a value loaded in from the backing bean would get reset when the field was blanked out and another component failed validation. I had to make a slight addition to BalusC's code to make it work.
有一个类似的问题,当字段被清空并且另一个组件验证失败时,从支持 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;
}
回答by Aaron
I'm thinking that during normal use people won't enter a valid date, submit, and then delete the date before submitting again. I realize that you found this during testing, but probably people are just trying to successfully fill out the form and not deleting stuff they already entered, in which case perhaps keeping the last valid value is the best functionality.
我认为在正常使用期间人们不会输入有效日期,提交,然后在再次提交之前删除日期。我意识到您在测试期间发现了这一点,但可能人们只是想成功填写表单而不是删除他们已经输入的内容,在这种情况下,保留最后一个有效值可能是最好的功能。
If you insist... It seems like the setter method "birthday" is never called because the value is invalid, and then when the page is redisplayed the current value of "birthday" is displayed (the current value being the valid value that was previously saved). Maybe you could write a custom validator that sets the value and THEN validates the value, but this wouldn't really make much sense. You would still have to validate the value first for cases when the users enters a text string like "yesterday" instead of a valid date, and then you would have to set the date to something based on that invalid value, and then you would have to add the message to the FacesContext. So the code would have to do something like the following.
1) validate the date value
2) if it's invalid then set the field to some value which makes sense and add an error message to FacesContext.
3) if it's valid then use it.
如果你坚持......似乎setter方法“生日”从未被调用,因为值无效,然后当页面重新显示时,“生日”的当前值被显示(当前值是有效值)以前保存)。也许您可以编写一个自定义验证器来设置值然后验证该值,但这并没有多大意义。对于用户输入诸如“昨天”之类的文本字符串而不是有效日期的情况,您仍然必须首先验证该值,然后您必须根据该无效值将日期设置为某个值,然后您将有将消息添加到 FacesContext。因此,代码必须执行以下操作。
1) 验证日期值
2) 如果它无效,则将该字段设置为某个有意义的值并向 FacesContext 添加错误消息。
3) 如果它有效,则使用它。
That's do-able, but weird because you're changing the value of the field even though the passed in value is invalid...
这是可行的,但很奇怪,因为即使传入的值无效,您也在更改字段的值...
回答by Matthias Reining
Aaron already descripes the behaviour.
Aaron 已经描述了这种行为。
The problem I've been described exists also by clicking the 'newContact' button. If the first submit is not valid (birthday was entered, name-field is empty) an error message is shown. ok.
通过单击“newContact”按钮,我所描述的问题也存在。如果第一次提交无效(输入生日,名称字段为空),则会显示错误消息。行。
Afterwards the 'newContact' Button do not refresh (clear) the view. Although the model was resetted (contact = new Contact()).
之后“newContact”按钮不会刷新(清除)视图。尽管模型已重置(contact = new Contact())。
I found some tipps here: http://wiki.apache.org/myfaces/ClearInputComponents
我在这里找到了一些提示:http://wiki.apache.org/myfaces/ClearInputComponents
Here is my solution:
这是我的解决方案:
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();
}
}