以下是该字段:
<h:inputText id="mobilePhoneNo"
value="#{newPatientBean.phoneNo}"
required="true"
requiredMessage="Required"
validator="#{mobilePhoneNumberValidator}"
validatorMessage="Not valid (validator)"
converter="#{mobilePhoneNumberConverter}"
converterMessage="Not valid (converter)"
styleClass="newPatientFormField"/>
验证员:
@Named
@ApplicationScoped
public class MobilePhoneNumberValidator implements Validator, Serializable
{
@Override
public void validate(FacesContext fc, UIComponent uic, Object o) throws ValidatorException
{
// This will appear in the log if/when this method is called.
System.out.println("mobilePhoneNumberValidator.validate()");
UIInput in = (UIInput) uic;
String value = in.getSubmittedValue() != null ? in.getSubmittedValue().toString().replace("-", "").replace(" ", "") : "";
if (!value.matches("04\\d{8}"))
{
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Please enter a valid mobile phone number.", null));
}
}
}
当我按下表单中的命令按钮时,我会收到以下行为:
在所有三种情况下,都会调用MobilePhoneNumberConverter.getAsObject()
。 MobilePhoneNumberValidator.validate()
从不被调用。当该字段为空时,它会忽略required="true"
属性并直接进行转换。
我原以为正确的行为是:
注意:支持bean是请求范围的,所以这里没有花哨的AJAX业务。
更新
这可能与javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL
被设置为true
?
答案 0 :(得分:13)
转换在验证之前发生。当值为null
或为空时,也会调用转换器。如果您要将null
值委托给验证器,那么您需要设计转换器,当提供的值为null
或为空时,它只会返回null
。
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.trim().isEmpty()) {
return null;
}
// ...
}
无关,您的验证器存在缺陷。您不应该从组件中提取提交的值。 不与转换器返回的值相同。正确提交和转换的值已作为第3个方法参数提供。
@Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
if (value == null) {
return; // This should normally not be hit when required="true" is set.
}
String phoneNumber = (String) value; // You need to cast it to the same type as returned by Converter, if any.
if (!phoneNumber.matches("04\\d{8}")) {
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Please enter a valid mobile phone number.", null));
}
}
答案 1 :(得分:4)
在阅读BalusC的评论后,我再次更新这篇文章。
我创建了一个小型演示应用程序,并查看阶段以及何时进行转换和验证。
查看:
<h:form>
<h:inputText value="#{demoBean.field}">
<f:converter converterId="demoConverter"/>
<f:validator validatorId="demoValidator"/>
</h:inputText>
<h:commandButton value="Submit" action="#{demoBean.demoAxn()}"/>
</h:form>
管理bean:
@ManagedBean
@SessionScoped
public class DemoBean implements Serializable {
private String field;
public DemoBean() {
System.out.println(Thread.currentThread().getStackTrace()[1]);
}
public String getField() {
System.out.println(Thread.currentThread().getStackTrace()[1]);
return field;
}
public void setField(String field) {
System.out.println(Thread.currentThread().getStackTrace()[1]);
this.field = field;
}
public String demoAxn() {
System.out.println(Thread.currentThread().getStackTrace()[1]);
return null;
}
}
转换器:
@FacesConverter(value="demoConverter")
public class DemoConverter implements Converter {
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
System.out.println(Thread.currentThread().getStackTrace()[1]);
return value;
}
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
System.out.println(Thread.currentThread().getStackTrace()[1]);
return (String) value;
}
}
验证
@FacesValidator(value="demoValidator")
public class DemoValidator implements Validator {
@Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
System.out.println(Thread.currentThread().getStackTrace()[1]);
}
}
阶段监听器:
public class DemoPhaseListener implements PhaseListener {
@Override
public void afterPhase(PhaseEvent event) {
System.out.println(Thread.currentThread().getStackTrace()[1]);
System.out.println("PhaseId: " + event.getPhaseId() + " ===============================\n\n");
}
@Override
public void beforePhase(PhaseEvent event) {
System.out.println("\n\nPhaseId: " + event.getPhaseId() + " ===============================");
System.out.println(Thread.currentThread().getStackTrace()[1]);
}
@Override
public PhaseId getPhaseId() {
return PhaseId.ANY_PHASE;
}
}
注册阶段监听器:
<lifecycle>
<phase-listener>pkg.DemoPhaseListener</phase-listener>
</lifecycle>
单击“提交”按钮时,使用该设置,输出为:
INFO: PhaseId: RESTORE_VIEW 1 =============================== INFO: pkg.DemoPhaseListener.beforePhase(DemoPhaseListener.java:17) INFO: pkg.DemoPhaseListener.afterPhase(DemoPhaseListener.java:10) INFO: PhaseId: RESTORE_VIEW 1 =============================== INFO: PhaseId: APPLY_REQUEST_VALUES 2 =============================== INFO: pkg.DemoPhaseListener.beforePhase(DemoPhaseListener.java:17) INFO: pkg.DemoPhaseListener.afterPhase(DemoPhaseListener.java:10) INFO: PhaseId: APPLY_REQUEST_VALUES 2 =============================== INFO: PhaseId: PROCESS_VALIDATIONS 3 =============================== INFO: pkg.DemoPhaseListener.beforePhase(DemoPhaseListener.java:17) INFO: pkg.DemoConverter.getAsObject(DemoConverter.java:13) INFO: pkg.DemoValidator.validate(DemoValidator.java:14) INFO: pkg.DemoBean.getField(DemoBean.java:17) INFO: pkg.DemoPhaseListener.afterPhase(DemoPhaseListener.java:10) INFO: PhaseId: PROCESS_VALIDATIONS 3 =============================== INFO: PhaseId: UPDATE_MODEL_VALUES 4 =============================== INFO: pkg.DemoPhaseListener.beforePhase(DemoPhaseListener.java:17) INFO: pkg.DemoBean.setField(DemoBean.java:22) INFO: pkg.DemoPhaseListener.afterPhase(DemoPhaseListener.java:10) INFO: PhaseId: UPDATE_MODEL_VALUES 4 =============================== INFO: PhaseId: INVOKE_APPLICATION 5 =============================== INFO: pkg.DemoPhaseListener.beforePhase(DemoPhaseListener.java:17) INFO: pkg.DemoBean.demoAxn(DemoBean.java:27) INFO: pkg.DemoPhaseListener.afterPhase(DemoPhaseListener.java:10) INFO: PhaseId: INVOKE_APPLICATION 5 =============================== INFO: PhaseId: RENDER_RESPONSE 6 =============================== INFO: pkg.DemoPhaseListener.beforePhase(DemoPhaseListener.java:17) INFO: pkg.DemoBean.getField(DemoBean.java:17) INFO: pkg.DemoConverter.getAsString(DemoConverter.java:20) INFO: pkg.DemoPhaseListener.afterPhase(DemoPhaseListener.java:10) INFO: PhaseId: RENDER_RESPONSE 6 ===============================
但是当改变以在转换器中抛出NPE时如下:
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
System.out.println(Thread.currentThread().getStackTrace()[1]);
throw new NullPointerException();
}
输出是:
INFO: PhaseId: RESTORE_VIEW 1 =============================== INFO: pkg.DemoPhaseListener.beforePhase(DemoPhaseListener.java:17) INFO: pkg.DemoPhaseListener.afterPhase(DemoPhaseListener.java:10) INFO: PhaseId: RESTORE_VIEW 1 =============================== INFO: PhaseId: APPLY_REQUEST_VALUES 2 =============================== INFO: pkg.DemoPhaseListener.beforePhase(DemoPhaseListener.java:17) INFO: pkg.DemoPhaseListener.afterPhase(DemoPhaseListener.java:10) INFO: PhaseId: APPLY_REQUEST_VALUES 2 =============================== INFO: PhaseId: PROCESS_VALIDATIONS 3 =============================== INFO: pkg.DemoPhaseListener.beforePhase(DemoPhaseListener.java:17) INFO: pkg.DemoConverter.getAsObject(DemoConverter.java:13) INFO: pkg.DemoPhaseListener.afterPhase(DemoPhaseListener.java:10) INFO: PhaseId: PROCESS_VALIDATIONS 3 =============================== INFO: pkg.DemoBean.getField(DemoBean.java:17)
但是堆栈跟踪会显示在结果视图上。