2011-05-05 8 views
9

Tengo la validación de Bean funcionando bien en mi aplicación. Ahora quiero verificar que un nuevo usuario no elija un nombre de usuario que ya haya sido elegido.¿Cómo se realiza la validación de JSF en actionListener o método de acción?

En el actionlistener tengo el código que verifica la base de datos, pero ¿cómo obligo al usuario a volver a la página en la que estaba si elige un nombre de usuario ya existente?

+0

Creo que, en primer lugar, no debería redireccionar a otra página, a menos que el nombre de usuario sea válido. – Benchik

Respuesta

14

Introducción

Usted puede hacerlo, pero los métodos JSF Ajax/acción/oyente son semánticamente el lugar equivocado hacer la validación. En realidad, no desea llegar tan lejos en el ciclo de vida JSF si tiene valores de entrada incorrectos en el formulario. Desea que el ciclo de vida JSF se detenga después de la fase de validación de JSF.

que desea utilizar una anotación de JSR303 Bean Validation (@NotNull y amigos) y/o validador de restricción, o utilizar un JSF Validator (required="true", <f:validateXxx>, etc) para que en su lugar. Se invocará correctamente durante la fase de validaciones JSF. De esta forma, cuando la validación falla, los valores del modelo no se actualizan y la acción comercial no se invoca y usted permanece en la misma página/vista.

Como no hay una anotación de validación de Bean estándar o un validador de JSF con el fin de verificar si un valor de entrada dado es único de acuerdo con la base de datos, necesitaría actualizar un validador personalizado para eso.

Voy a mostrarle a ambos lados cómo crear un validador personalizado que compruebe la exclusividad del nombre de usuario.

personalizada JSR303 Bean Validation anotación

En primer lugar crear una anotación personalizada @Username restricción:

@Constraint(validatedBy = UsernameValidator.class) 
@Documented 
@Retention(RetentionPolicy.RUNTIME) 
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) 
public @interface Username { 
    String message() default "Username already exists"; 
    Class<?>[] groups() default {}; 
    Class<? extends Payload>[] payload() default {}; 
} 

Con este validador restricción (nota: @EJB o @Inject dentro de un ConstraintValidator funciona sólo desde CDI 1.1; así que si Todavía estoy en CDI 1.0 entonces que había necesidad de agarrar manualmente desde JNDI):

public class UsernameValidator implements ConstraintValidator<Username, String> { 

    @EJB 
    private UserService service; 

    @Override 
    public void initialize(Username constraintAnnotation) { 
     // If not on CDI 1.1 yet, then you need to manually grab EJB from JNDI here. 
    } 

    Override 
    public boolean isValid(String username, ConstraintValidatorContext context) { 
     return !service.exist(username); 
    } 

} 

Finalmente lo utilizan de la siguiente manera en el modelo:

@Username 
private String username; 

personalizado JSF Validador

Una alternativa es utilizar una costumbre Validador JSF Sólo implementar la interfaz JSF Validator:

@ManagedBean 
@RequestScoped 
public class UsernameValidator implements Validator { 

    @EJB 
    private UserService userService; 

    @Override 
    public void validate(FacesContext context, UIComponent component, Object submittedAndConvertedValue) throws ValidatorException { 
     String username = (String) submittedAndConvertedValue; 

     if (username == null || username.isEmpty()) { 
      return; // Let required="true" or @NotNull handle it. 
     } 

     if (userService.exist(username)) { 
      throw new ValidatorException(new FacesMessage("Username already in use, choose another")); 
     } 
    } 

} 

Finalmente utilizarlo de la siguiente manera a la vista:

<h:inputText id="username" ... validator="#{usernameValidator}" /> 
<h:message for="username" /> 

Tenga en cuenta que normalmente tendría que utilizar una anotación de @FacesValidator en la clase Validator, pero hasta la próxima JSF 2.3 , no es compatible con @EJB o @Inject. Vea también How to inject in @FacesValidator with @EJB, @PersistenceContext, @Inject, @Autowired.

+1

Gracias BalusC. ¿Este validador se llamará antes o después de que la validación del frijol haga sus verificaciones o la orden no se especifique? –

+2

La validación de JSF se ejecuta antes de la validación de Bean. Pero si el valor es 'nulo' o está vacío, no se ejecutará la validación JSF que no sea' required'. Entonces, si tienes '@ NotNull' sin' required = "true" ', entonces se ejecutará primero. Pero si no está vacío y tiene, por ejemplo, un '@ Pattern', entonces se llamará primero al validador JSF. – BalusC

+0

Eres increíble. pero, ¿no puedo simplemente usar el validador jsf normal, sino solo validar la propiedad usando la validación de bean? Si es válido, entonces puedo consultar la base de datos. Probé el método validateProperty de la clase Validator, pero no pude hacer que funcionara porque estaba pidiendo grupos y otra semántica que no entendí –

2

Es mejor usar el método de acción en lugar del actionListener para este propósito. Luego puede devolver null (recarga la página que desencadenó la acción) desde este método si existe el nombre de usuario. He aquí un ejemplo:

en el facelet:

<h:commandButton action="#{testBean.doAction}" value="and... Action"/> 

en el grano:

public String doAction() { 
    if (userExists) { 
    return null; 
    } else { 
    // go on processing ... 
    } 
} 
0

puede definir un caso de navegación en el archivo faces-config.xml. Esto le permitirá redirigir al usuario a una página dada, dependiendo del valor de retorno del bean.

En el siguiente ejemplo, un suer se redirige a una de dos páginas según el valor de retorno de "myMethod()".

<navigation-rule> 
    <from-view-id>/index.xhtml</from-view-id> 
    <navigation-case> 
    <from-action>#{myBean.myMethod()}</from-action> 
    <from-outcome>true</from-outcome> 
    <to-view-id>/correct.xhtml</to-view-id> 
    </navigation-case> 
    <navigation-case> 
    <from-action>#{myBean.myMethod()}</from-action> 
    <from-outcome>false</from-outcome> 
    <to-view-id>/error.xhtml</to-view-id> 
    </navigation-case> 
</navigation-rule> 
3

Sí, puedes. Puede hacer la validación en el método de escucha de acción, agregar mensajes de rostros si su validación personalizada falló, luego llamar al FacesContext.validationFailed() justo antes de regresar.

El único problema con esta solución es que ocurre después de la validación de JSF y de la validación de beans. Es decir, es después de la fase de validación. Si tiene múltiples oyentes de acción, diga listener1 y listener2: si su validación personalizada en listener1 falló, continuará ejecutando listener2. Pero después de todo, obtendrás la validación de la respuesta AJAX.

+0

Si esto se hace en un actionListener como se comentó, entonces necesitarás hacer Asegúrese de que el método de acción comprueba 'facesContext.isValidationFailed()' continuando. – Lucas

Cuestiones relacionadas