JSF转换器导致验证器被忽略

时间:2012-01-03 22:49:02

标签: java jsf-2

以下是该字段:

<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

有关

2 个答案:

答案 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)

但是堆栈跟踪会显示在结果视图上。