Как выполнить проверку JSF в actionListener или методе действия?
В моем приложении хорошо работает проверка Бинов. Теперь я хочу убедиться, что новый пользователь не выбирает имя пользователя, которое уже было выбрано.
В actionlistener у меня есть код, который проверяет базу данных, но как заставить пользователя вернуться на страницу, на которой он находился, если он выберет уже существующее имя пользователя?
4 ответа
Вступление
Вы можете сделать это, но методы JSF ajax/action/listener семантически не подходят для проверки. Вы на самом деле не хотите заходить так далеко в жизненном цикле JSF, если вы неправильно указали значения в форме. Вы хотите, чтобы жизненный цикл JSF остановился после фазы проверки JSF.
Вы хотите использовать аннотацию Bean Validation JSR303 (@NotNull
и друзья) и / или средство проверки ограничений, или использовать JSF Validator
(required="true"
, <f:validateXxx>
и т.д.) для этого вместо этого. Он будет корректно вызываться на этапе проверки JSF. Таким образом, при сбое проверки значения модели не обновляются, и бизнес-действие не вызывается, и вы остаетесь на той же странице / представлении.
Так как нет стандартной аннотации Bean Validation или JSF Validator для проверки того, является ли заданное входное значение уникальным в соответствии с базой данных, вам необходимо создать собственный валидатор для этого.
В обоих случаях я покажу, как создать собственный валидатор, который проверяет уникальность имени пользователя.
Пользовательская аннотация аннотации bean-компонента JSR303
Сначала создайте кастом @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 Validator
Альтернативой является использование собственного валидатора 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.
Да, ты можешь. Вы можете выполнить проверку в методе прослушивателя действий, добавить сообщения лиц, если пользовательская проверка не удалась, затем вызвать FacesContext.validationFailed()
как раз перед возвращением.
Единственная проблема с этим решением состоит в том, что это происходит после проверки JSF и проверки bean-компонента. Т.е. это после этапа валидации. Если у вас есть несколько слушателей действий, скажем, listener1 и listener2: если ваша пользовательская проверка в listener1 не удалась, она продолжит выполнение listener2. Но в конце концов, вы получите validationFailed в ответе AJAX.
Для этой цели лучше использовать метод действия вместо actionListener. Тогда вы можете вернуться null
(перезагружает страницу, которая запустила действие) из этого метода, если имя пользователя существует. Вот пример:
в лицевой стороне:
<h:commandButton action="#{testBean.doAction}" value="and... Action"/>
в бобе:
public String doAction() {
if (userExists) {
return null;
} else {
// go on processing ...
}
}
Если вы хотите оставить отзыв для конечного пользователя:
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";
}
Вы можете определить навигационный регистр в файле face-config.xml. Это позволит вам перенаправить пользователя на данную страницу в зависимости от возвращаемого значения бина.
В приведенном ниже примере 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>