如何在actionListener或action方法中执行JSFvalidation?
我的Beanvalidation在我的应用程序中很好地工作。 现在我想检查一个新用户没有select已经被select的用户名。
在actionlistener中,我有检查数据库的代码,但如果用户select一个已经存在的用户名,我该如何强制用户返回到他们所在的页面呢?
介绍
您可以这样做,但JSF ajax / action / listener方法在语义上是错误的地方做validation。 如果您在表单中input值错误,您实际上不希望在JSF生命周期中获得如此巨大的收益。 您希望JSF生命周期在JSFvalidation阶段后停止。
您希望使用JSR303 Bean Validation注解( @NotNull
和friends)和/或约束validation程序,或者使用JSF Validator
( required="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>