JSF转换器导致validation器被忽略

这是领域:

 

validation者:

 @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)); } } } 

当我按下窗体中的命令按钮时,我得到以下行为:

  • 当该字段为空时,消息为“无效(转换器)”。
  • 当字段具有有效条目时,消息为“无效(validation器)”。
  • 当字段包含无效条目时,消息为“无效(转换器)”。

在所有三种情况下,都会调用MobilePhoneNumberConverter.getAsObject()永远不会调用MobilePhoneNumberValidator.validate() 。 当该字段为空时,它会忽略required="true"属性并直接进行转换。

我原以为正确的行为是:

  • 当该字段为空时,该消息应为“必需”。
  • 当字段具有有效条目时,根本不应该有消息。
  • 当字段具有无效条目时,该消息应为“无效(validation器)”。
  • 如果通过某种可能性,通过转换传递的validation没有,则消息应为“无效(转换器)”。

注意:支持bean是请求范围的,因此这里没有花哨的AJAX业务。

更新:

它可能与javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL设置为true吗?

转换在validation之前发生。 当值为null或为空时,也将调用转换器。 如果要将null值委托给validation器,则需要设计转换器,当提供的值为null或为空时,它只返回null

 @Override public Object getAsObject(FacesContext context, UIComponent component, String value) { if (value == null || value.trim().isEmpty()) { return null; } // ... } 

具体问题无关 ,您的validation器存在缺陷。 您不应该从组件中提取提交的值。 它与转换器返回的值不同。 正确提交和转换的值已作为第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)); } } 

阅读BalusC的评论后,我再次更新这篇文章。

我创建了一个小型演示应用程序,以查看阶段以及何时进行转换和validation。

视图:

        

管理豆:

 @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; } } 

validation器:

 @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; } } 

注册阶段监听器:

  pkg.DemoPhaseListener  

使用该设置单击“提交”按钮时,输出为:

信息:PhaseId:RESTORE_VIEW 1 ===============================
信息:pkg.DemoPhaseListener.beforePhase(DemoPhaseListener.java:17)
信息:pkg.DemoPhaseListener.afterPhase(DemoPhaseListener.java:10)
信息:PhaseId:RESTORE_VIEW 1 ===============================

信息:PhaseId:APPLY_REQUEST_VALUES 2 ===============================
信息:pkg.DemoPhaseListener.beforePhase(DemoPhaseListener.java:17)
信息:pkg.DemoPhaseListener.afterPhase(DemoPhaseListener.java:10)
信息:PhaseId:APPLY_REQUEST_VALUES 2 ===============================

信息:PhaseId:PROCESS_VALIDATIONS 3 ===============================
信息:pkg.DemoPhaseListener.beforePhase(DemoPhaseListener.java:17)
信息:pkg.DemoConverter.getAsObject(DemoConverter.java:13)
信息:pkg.DemoValidator.validate(DemoValidator.java:14)
信息:pkg.DemoBean.getField(DemoBean.java:17)
信息:pkg.DemoPhaseListener.afterPhase(DemoPhaseListener.java:10)
信息:PhaseId:PROCESS_VALIDATIONS 3 ===============================

信息:PhaseId:UPDATE_MODEL_VALUES 4 ===============================
信息:pkg.DemoPhaseListener.beforePhase(DemoPhaseListener.java:17)
信息:pkg.DemoBean.setField(DemoBean.java:22)
信息:pkg.DemoPhaseListener.afterPhase(DemoPhaseListener.java:10)
信息:PhaseId:UPDATE_MODEL_VALUES 4 ===============================

信息:PhaseId:INVOKE_APPLICATION 5 ===============================
信息:pkg.DemoPhaseListener.beforePhase(DemoPhaseListener.java:17)
信息:pkg.DemoBean.demoAxn(DemoBean.java:27)
信息:pkg.DemoPhaseListener.afterPhase(DemoPhaseListener.java:10)
信息:PhaseId:INVOKE_APPLICATION 5 ===============================

信息:PhaseId:RENDER_RESPONSE 6 ===============================
信息:pkg.DemoPhaseListener.beforePhase(DemoPhaseListener.java:17)
信息:pkg.DemoBean.getField(DemoBean.java:17)
信息:pkg.DemoConverter.getAsString(DemoConverter.java:20)
信息:pkg.DemoPhaseListener.afterPhase(DemoPhaseListener.java:10)
信息: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(); } 

输出是:

信息:PhaseId:RESTORE_VIEW 1 ===============================
信息:pkg.DemoPhaseListener.beforePhase(DemoPhaseListener.java:17)
信息:pkg.DemoPhaseListener.afterPhase(DemoPhaseListener.java:10)
信息:PhaseId:RESTORE_VIEW 1 ===============================

信息:PhaseId:APPLY_REQUEST_VALUES 2 ===============================
信息:pkg.DemoPhaseListener.beforePhase(DemoPhaseListener.java:17)
信息:pkg.DemoPhaseListener.afterPhase(DemoPhaseListener.java:10)
信息:PhaseId:APPLY_REQUEST_VALUES 2 ===============================

信息:PhaseId:PROCESS_VALIDATIONS 3 ===============================
信息:pkg.DemoPhaseListener.beforePhase(DemoPhaseListener.java:17)
信息:pkg.DemoConverter.getAsObject(DemoConverter.java:13)
信息:pkg.DemoPhaseListener.afterPhase(DemoPhaseListener.java:10)
信息:PhaseId:PROCESS_VALIDATIONS 3 ===============================

信息:pkg.DemoBean.getField(DemoBean.java:17)

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