2012-01-03 12 views
6

Aquí está el campo:convertidor de JSF causa validador (s) a ser ignorado

<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"/> 

Y el validador:

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

Al pulsar el botón de comando dentro de la forma, me sale el siguiente comportamiento :

  • Cuando el campo está en blanco, el mensaje es "No válido (convertidor)".
  • Cuando el campo tiene una entrada válida, el mensaje es "No válido (validador)".
  • Cuando el campo tiene una entrada no válida, el mensaje es "No válido (convertidor)".

En los tres casos, se llama MobilePhoneNumberConverter.getAsObject(). MobilePhoneNumberValidator.validate() es nunca llamado. Y cuando el campo está en blanco, ignora el atributo required="true" y procede directamente a la conversión.

yo habría pensado que el comportamiento adecuado sería:

  • Cuando el campo está en blanco, el mensaje debe ser "necesario".
  • Cuando el campo tiene una entrada válida, no debe haber ningún mensaje.
  • Cuando el campo tiene una entrada no válida, el mensaje debe ser "No válido (validador)".
  • Si, por casualidad, la validación aprobada por la conversión no, el mensaje debería ser "No válido (convertidor)".

Nota: El bean de respaldo tiene un alcance de solicitud, por lo que no hay ningún asunto de AJAX de lujo pasando aquí.

Actualización:

Podría tener algo que ver con javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL que se puso a true?

Respuesta

12

La conversión ocurre antes de la validación. Los convertidores también se llamarán cuando el valor sea null o esté vacío. Si desea delegar el valor null a los validadores, debe diseñar los convertidores que devuelven null cuando el valor suministrado es null o está vacío.

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

    // ... 
} 

Sin relación al problema concreto, el validador tiene un defecto. No debe extraer el valor enviado del componente. Es no el mismo valor que devuelve el convertidor. El valor enviado y convertido a la derecha ya está disponible como el tercer argumento del método.

@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

Gracias @BalusC. A pesar de venir mucho más tarde y básicamente resumir la larga conversación adjunta a la respuesta de βнɛƨн Ǥʋяʋиɢ (que yo había aceptado), preferiría que la respuesta más útil fuera la que se marcó como aceptada para el beneficio de otros que se encuentran con esta pregunta. Lo siento βнɛƨн Ǥʋяʋиɢ :( –

+0

De nada, tenga en cuenta que actualicé la respuesta después de haber examinado de cerca la implementación de su validador, lo que también es esencialmente incorrecto. – BalusC

+0

@BalusC: Gracias por corregirme. Dado que, totalmente engañoso, estoy editando mi respuesta. –

3

Después de leer el comentario de BalusC, estoy actualizando esta publicación nuevamente.

Creé una pequeña aplicación de demostración y para ver las fases y cuándo ocurren la conversión y la validación.

Vista:

<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 administrado:

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

Convertidor:

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

Validador:

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

} 

Fase oyente:

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

registró el detector de fase:

<lifecycle> 
    <phase-listener>pkg.DemoPhaseListener</phase-listener> 
</lifecycle> 

Con esa configuración cuando el "Enviar" se hace clic en el botón, la salida es:

 
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 =============================== 

Pero cuando el cambio de marca para lanzar un NPE en el convertidor de la siguiente manera:

@Override 
public Object getAsObject(FacesContext context, UIComponent component, String value) { 
    System.out.println(Thread.currentThread().getStackTrace()[1]);    
    throw new NullPointerException(); 
} 

la salida es:

 
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) 

Pero el StackTrace se visualiza en la vista resultante.

+0

No sé lo que está pasando aquí ... Me cambiaron los convertidores y validadores de ser anotada la forma de JSF 2.0. Hubo un tiempo no hace mucho tiempo cuando esto no cambiaba nada. Tal vez GlassFish 3.1.1 me está jodiendo. De todos modos, con las anotaciones "correctas", el atributo 'required' aún no funciona, sino que va directamente a la conversión. De hecho, no hay absolutamente ningún escenario ahora en el que se produzca la validación. ¿Debo entender que esto significa que los convertidores hacen que la validación se omita? –

+0

"¿Debo entender que esto significa que los convertidores hacen que la validación se salte?" No. Hacen diferentes cosas sin relación, y deberíamos poder ejecutarlos juntos. Pero, en caso de falla, jsf detiene el procesamiento de solicitud y se salta otra fase y salta para procesar la respuesta. ' –

+0

"No. Hacen diferentes cosas sin relación, y deberíamos poder ejecutarlas juntas". Lo estoy viendo con mis propios ojos: no están ** trabajando juntos. "Pero, en caso de falla, jsf detiene el procesamiento de solicitud y se salta otra fase y salta para procesar la respuesta". Sin embargo, ¿cómo es esto relevante?La validación y conversión se producen en la misma fase y jsf ejecutará al menos esta fase hasta su finalización (para validar * todos * los valores enviados y, por lo tanto, mostrará * todos * los errores de validación/conversión) antes de omitir las fases restantes. –

Cuestiones relacionadas