如何在actionListener或action方法中执行JSF验证?

时间:2011-05-05 03:12:15

标签: validation jsf bean-validation

我的Bean验证在我的应用程序中运行良好。现在我想检查新用户是否选择了已经选择的用户名。

在actionlistener中,我有检查数据库的代码,但是如果用户选择已经存在的用户名,如何强制用户被发送回他们所在的页面?

5 个答案:

答案 0 :(得分:18)

简介

可以这样做,但JSF ajax / action / listener方法在语义上是错误的进行验证的地方。如果表单中的输入值错误,您实际上不希望在JSF生命周期中达到那么远。您希望JSF生命周期在JSF验证阶段后停止。

您想使用JSR303 Bean验证注释(@NotNull和朋友)和/或约束验证器,或使用JSF Validatorrequired="true"<f:validateXxx>等)而是为了那个。它将在JSF验证阶段正确调用。这样,当验证失败时,模型值不会更新,也不会调用业务操作,您将保持在同一页面/视图中。

由于没有标准的Bean Validation批注或JSF Validator来检查给定的输入值是否根据数据库是唯一的,因此您需要为此自定义验证器。

我将以两种方式展示如何创建一个自定义验证器来检查用户名的唯一性。

自定义JSR303 Bean验证注释

首先创建自定义@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 {};
}

使用此约束验证程序(注意:@EJB@InjectConstraintValidator仅适用于CDI 1.1;因此,如果您仍然使用CDI 1.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;

自定义JSF验证器

另一种方法是使用自定义JSF验证器。只需实现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" />

请注意,您通常会在@FacesValidator课程中使用Validator注释,但在即将推出的JSF 2.3之前,它不支持@EJB@Inject。另请参阅How to inject in @FacesValidator with @EJB, @PersistenceContext, @Inject, @Autowired

答案 1 :(得分:4)

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

此解决方案的唯一问题是,它发生在JSF验证和bean验证之后。即,它是在验证阶段之后。如果你有多个动作监听器,比如listener1和listener2:如果你在listener1中的自定义验证失败,它将继续执行listener2。但毕竟,你将在AJAX响应中得到validationFailed。

答案 2 :(得分:1)

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

小平面中的

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

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

答案 3 :(得分:0)

如果要向最终用户提供反馈:

xhtml:

    <p:commandButton value="Go" process="@this" action="#{myBean.checkEntity()}" oncomplete="if(args.validationFailed){PF('widgetOldInfoNotice').show();}"/>

    <p:confirmDialog id="dialogOldInfoNotice" header="NOTICE" severity="alert" widgetVar="widgetOldInfoNotice">
    -- feedback message--
<p:button value="Ok" onclick="PF('widgetOldInfoNotice').hide();"/>
    </p:confirmDialog>

bean:

public String checkEntity() {
    if (!dao.whateverActionToValidateEntity(selectedEntity)) {
        FacesContext context = FacesContext.getCurrentInstance();
        context.validationFailed();
        return "";
    }
    return "myPage.xhtml";
}

答案 4 :(得分:-1)

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

在下面的例子中,suer被重定向到两个页面中的一个,具体取决于“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>