我的 Bean 验证在我的应用程序中运行良好。现在我想检查一个新用户是否没有选择已经选择的用户名。
在 actionlistener 中,我有检查数据库的代码,但是如果用户选择了一个已经存在的用户名,我如何强制用户被发送回他们所在的页面?
我的 Bean 验证在我的应用程序中运行良好。现在我想检查一个新用户是否没有选择已经选择的用户名。
在 actionlistener 中,我有检查数据库的代码,但是如果用户选择了一个已经存在的用户名,我如何强制用户被发送回他们所在的页面?
您可以这样做,但 JSF ajax/action/listener 方法在语义上是错误的验证位置。如果您在表单中输入了错误的值,您实际上并不想在 JSF 生命周期中走得那么远。您希望 JSF 生命周期在 JSF 验证阶段之后停止。
您想使用 JSR303 Bean Validation 注释(@NotNull
和朋友)和/或约束验证器,或者使用 JSF Validator
(required="true"
,<f:validateXxx>
等)代替。它将在 JSF 验证阶段正确调用。这样,当验证失败时,模型值不会更新,业务操作不会被调用,并且您会停留在同一页面/视图中。
由于没有标准的 Bean Validation 注释或 JSF Validator 来检查给定的输入值是否根据数据库是唯一的,因此您需要为此自行开发自定义验证器。
我将通过两种方式展示如何创建一个自定义验证器来检查用户名的唯一性。
首先创建一个自定义@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 验证器。只需实现 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。
是的你可以。您可以在动作侦听器方法中进行验证,如果您的自定义验证失败,则添加面孔消息,然后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>