如何在actionListener或action方法中执行JSFvalidation?

我的Beanvalidation在我的应用程序中很好地工作。 现在我想检查一个新用户没有select已经被select的用户名。

在actionlistener中,我有检查数据库的代码,但如果用户select一个已经存在的用户名,我该如何强制用户返回到他们所在的页面呢?

介绍

可以这样做,但JSF ajax / action / listener方法在语义上是错误的地方做validation。 如果您在表单中input值错误,您实际上不希望在JSF生命周期中获得如此巨大的收益。 您希望JSF生命周期在JSFvalidation阶段后停止。

您希望使用JSR303 Bean Validation注解( @NotNull和friends)和/或约束validation程序,或者使用JSF Validatorrequired="true"<f:validateXxx>等)。 它将在JSFvalidation阶段正确调用。 这样,当validation失败时,模型值不会更新,业务操作也不会被调用,而是停留在同一个页面/视图中。

由于没有标准的Bean Validation注解或者JSF Validator来检查给定的input值是否是唯一的数据库,所以你需要为它定制一个validation器。

我将为这两种方式显示如何创build一个自定义validation程序,检查用户名的唯一性。

自定义JSR303 Beanvalidation注释

首先创build一个自定义的@Username约束注解:

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

有了这个约束validation器(注意:在一个ConstraintValidator @EJB@Inject只能在@Inject工作,所以如果你仍然在CDI1.0上,那么你需要从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); } } 

最后在模型中使用它如下:

 @Username private String username; 

自定义JSFvalidation器

另一种方法是使用自定义的JSFvalidation器。 只需实现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")); } } } 

最后使用它如下所示:

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

请注意,您通常会在Validator类上使用@FacesValidator批注,但在即将到来的JSF 2.3之前,它不支持@EJB@Inject 。 另请参见如何使用@EJB,@PersistenceContext,@Inject,@Autowired注入@FacesValidator 。

是的你可以。 您可以在动作侦听器方法中进行validation,如果自定义validation失败,则添加faces消息,然后在返回之前调用FacesContext.validationFailed()

这个解决scheme的唯一问题是,它发生在JSFvalidation和beanvalidation之后。 也就是说,在validation阶段之后。 如果您有多个操作侦听器,请说listener1和listener2:如果您在listener1中的自定义validation失败,它将继续执行listener2。 但毕竟,你会在AJAX响应中得到validationFailed。

为了这个目的,最好使用action方法而不是actionListener。 然后,如果用户名存在,则可以从此方法返回null (重新加载触发该操作的页面)。 这是一个例子:

在facelet:

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

在豆:

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

您可以在faces-config.xml文件中定义一个导航案例。 这将允许您根据bean的返回值将用户redirect到给定的页面。

在下面的例子中,根据“myMethod()”的返回值,suer被redirect到两页中的一页。

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