2012-01-03 60 views
6

這裏的領域:JSF轉換導致驗證器(S)被忽略

<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

回答

12

轉換髮生在驗證之前。當值爲null或空時,轉換器也會被調用。如果您想將null值委託給驗證器,那麼您需要設計您的轉換器,當提供的值爲null或空時,它只返回null

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

    // ... 
} 

無關到具體的問題,你的驗證器有一個缺陷。您不應該從組件提取提交的值。它是而不是與轉換器返回的值相同。正確的提交和轉換值已經作爲第三方法參數。

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

謝謝@BalusC。儘管在很長時間之後,基本上總結了βɛƨɛƨʋǤʋʋʋɢ的答案(我已接受)的長時間對話,但我寧願將最有用的答案作爲接受這個問題的其他人的好處。對不起,請稍候:( – 2012-01-04 14:22:57

+0

)不客氣,請注意,在仔細查看你的驗證器實現後,我更新了答案,這在本質上也是錯誤的。 – BalusC 2012-01-04 15:38:55

+0

@BalusC:感謝您糾正我自從,完全誤導我編輯我的回答 – 2012-01-04 15:48:44

3

在閱讀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) 

但堆棧跟蹤被顯示在所得到的圖。

+0

我不知道這裏發生了什麼......我將轉換器和驗證器更改爲註釋爲JSF 2.0的方式。不久之前有一段時間,這完全沒有區別。也許GlassFish 3.1.1正在與我screw screw。無論如何,對於「正確的」註釋,「required」屬性仍然不起作用,而是直接轉換。事實上,現在絕對沒有驗證發生的場景。我是否應該認爲轉換器會導致驗證被忽略? – 2012-01-03 23:39:58

+0

「我應該認爲轉換器是否會導致驗證被忽略?」不,他們做不同的不相關的東西,我們應該能夠一起運行它們。但是,如果失敗,jsf會停止請求處理並跳過其他階段並跳轉以呈現響應。 – 2012-01-04 00:20:04

+0

「不,他們做不同的不相關的東西,我們應該能夠一起運行它們。」我親眼看到它 - 它們不是**一起工作。 「但是,如果失敗,jsf會停止請求處理並跳過其他階段並跳轉以呈現響應。」但是,這有什麼相關?驗證和轉換髮生在同一階段,jsf將至少運行此階段以完成(驗證*所有*提交的值,因此顯示*所有*驗證/轉換錯誤),然後跳過其餘階段。 – 2012-01-04 00:27:24

相關問題