15

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

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

4

5 回答 5

18

介绍

可以这样做,但 JSF ajax/action/listener 方法在语义上是错误的验证位置。如果您在表单中输入了错误的值,您实际上并不想在 JSF 生命周期中走得那么远。您希望 JSF 生命周期在 JSF 验证阶段之后停止。

您想使用 JSR303 Bean Validation 注释(@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或仅@InjectConstraintValidatorCDI 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 验证器。只需实现 JSFValidator接口:

@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. 另请参阅如何使用 @EJB、@PersistenceContext、@Inject、@Autowired 注入 @FacesValidator

于 2011-05-05T11:50:28.857 回答
4

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

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

于 2012-02-09T14:35:58.463 回答
1

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

在小面中:

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

在豆子里:

public String doAction() {
   if (userExists) {
     return null;
   } else {
     // go on processing ...
   }
}
于 2011-05-05T06:36:18.993 回答
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>

豆:

public String checkEntity() {
    if (!dao.whateverActionToValidateEntity(selectedEntity)) {
        FacesContext context = FacesContext.getCurrentInstance();
        context.validationFailed();
        return "";
    }
    return "myPage.xhtml";
}
于 2019-06-20T23:52:24.717 回答
-1

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

在下面的示例中,根据“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>
于 2011-05-05T08:56:39.863 回答