Как выполнить проверку JSF в actionListener или методе действия?

У меня есть проверка Bean, хорошо работающая в моем приложении. Теперь я хочу проверить, что новый пользователь не выбирает имя пользователя, которое уже было выбрано.

В actionlistener у меня есть код, который проверяет базу данных, но как заставить пользователя вернуться на страницу, на которой он был, если он выберет уже существующее имя пользователя?


person Farouk Alhassan    schedule 05.05.2011    source источник
comment
Я думаю, что вам не следует перенаправлять на другую страницу, если имя пользователя недействительно.   -  person Benchik    schedule 05.05.2011


Ответы (5)


Введение

Вы можете сделать это, но методы JSF ajax/action/listener семантически являются неправильным местом для проверки. На самом деле вы не хотите заходить так далеко в жизненном цикле JSF, если у вас неправильные входные значения в форме. Вы хотите, чтобы жизненный цикл JSF остановился после фазы проверки JSF.

Вы хотите использовать аннотацию JSR303 Bean Validation (@NotNull и друзья) и/или валидатор ограничений, или вместо этого использовать JSF Validator (required="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 или @Inject внутри ConstraintValidator работает только с 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. См. также Как внедрить в @FacesValidator с помощью @EJB, @PersistenceContext, @Inject, @Autowired.

person BalusC    schedule 05.05.2011
comment
Спасибо BalusC. Будет ли этот валидатор вызываться до или после проверки bean-компонента, или порядок не указан? - person Farouk Alhassan; 05.05.2011
comment
Проверка JSF выполняется перед проверкой Bean. Но если значение равно null или пусто, проверка JSF, отличная от required, выполняться не будет. Итак, если у вас есть @NotNull без required="true", то он будет выполнен первым. Но если он не пуст и у вас есть, например, @Pattern, то сначала будет вызван валидатор JSF. - person BalusC; 05.05.2011
comment
Ты обалденный. но могу ли я не просто использовать обычный валидатор jsf, а только проверять свойство, используя проверку бина? Если он действителен, я могу проверить базу данных. Я попробовал метод validateProperty класса Validator, но я не мог просто заставить его работать, потому что он запрашивал группы и другую семантику, которую я не понимал. - person Farouk Alhassan; 05.05.2011

Да, ты можешь. Вы можете выполнить проверку в методе прослушивания действий, добавить сообщения о лицах, если ваша пользовательская проверка не удалась, а затем вызвать FacesContext.validationFailed() непосредственно перед возвратом.

Единственная проблема с этим решением заключается в том, что это происходит после проверки JSF и проверки bean-компонента. То есть, это после этапа проверки. Если у вас есть несколько прослушивателей действий, скажем, listener1 и listener2: если ваша пользовательская проверка в listener1 не удалась, она продолжит выполнение listener2. Но в конце концов вы получите validationFailed в ответе AJAX.

person Xiè Jìléi    schedule 09.02.2012
comment
Если это делается в actionListener, как указано в комментарии, вам нужно убедиться, что метод действия проверяет facesContext.isValidationFailed() продолжение. - person Lucas; 24.01.2013

Для этой цели лучше использовать метод действия вместо actionListener. Затем вы можете вернуть null (перезагружает страницу, вызвавшую действие) из этого метода, если имя пользователя существует. Вот пример:

на лицевой стороне:

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

в фасоли:

public String doAction() {
   if (userExists) {
     return null;
   } else {
     // go on processing ...
   }
}
person Matt Handy    schedule 05.05.2011
comment
Проблема здесь заключается в обеспечении правильной обратной связи с конечным пользователем. - person Kukeltje; 20.03.2019

Если вы хотите оставить отзыв конечному пользователю:

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";
}
person onClick    schedule 20.06.2019

Вы можете определить случай навигации в файле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>
person David    schedule 05.05.2011