[英]How to perform JSF validation in actionListener or action method?
我的 Bean 验证在我的应用程序中运行良好。 现在我想检查一个新用户是否没有选择已经选择的用户名。
在 actionlistener 中,我有检查数据库的代码,但是如果用户选择了一个已经存在的用户名,我如何强制用户被发送回他们所在的页面?
你可以这样做,但是 JSF ajax/action/listener 方法在语义上是错误的验证位置。 如果您在表单中输入错误的值,您实际上不想在 JSF 生命周期中走得那么远。 您希望 JSF 生命周期在 JSF 验证阶段之后停止。
您想使用 JSR303 Bean 验证注释( @NotNull
和朋友)和/或约束验证器,或使用 JSF Validator
( required="true"
, <f:validateXxx>
等)代替。 它将在 JSF 验证阶段正确调用。 这样,当验证失败时,model 值不会更新,也不会调用业务操作,您会停留在同一页面/视图中。
由于没有标准的 Bean 验证注释或 JSF 验证器来检查给定的输入值是否根据数据库是唯一的,因此您需要为此自行开发一个自定义验证器。
我将通过两种方式展示如何创建一个自定义验证器来检查用户名的唯一性。
首先创建一个自定义的@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 {};
}
使用此约束验证器(注意: ConstraintValidator
中的@EJB
或@Inject
仅从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);
}
}
最后在model中使用如下:
@Username
private String username;
另一种方法是使用自定义 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" />
请注意,您通常会在Validator
class 上使用@FacesValidator
注释,但在即将发布的 JSF 2.3 之前,它不支持@EJB
或@Inject
。 另请参阅如何使用 @EJB、@PersistenceContext、@Inject、@Autowired 注入 @FacesValidator 。
是的你可以。 您可以在动作侦听器方法中进行验证,如果您的自定义验证失败,则添加面孔消息,然后在返回之前调用FacesContext.validationFailed()
。
此解决方案的唯一问题是,它发生在 JSF 验证和 bean 验证之后。 即,它在验证阶段之后。 如果您有多个动作侦听器,例如 listener1 和 listener2:如果您在 listener1 中的自定义验证失败,它将继续执行 listener2。 但毕竟,你会在 AJAX 响应中得到验证失败。
为此目的,最好使用 action 方法而不是 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";
}
您可以在 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>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.