我的Bean驗證在我的應用程序中很好地工作。現在我想檢查一個新用戶不選擇已經選擇的用戶名。如何在actionListener或action方法中執行JSF驗證?
在actionlistener中,我有檢查數據庫的代碼,但如果用戶選擇已存在的用戶名,我如何強制用戶返回到他們所在的頁面?
我的Bean驗證在我的應用程序中很好地工作。現在我想檢查一個新用戶不選擇已經選擇的用戶名。如何在actionListener或action方法中執行JSF驗證?
在actionlistener中,我有檢查數據庫的代碼,但如果用戶選擇已存在的用戶名,我如何強制用戶返回到他們所在的頁面?
您可以做到這一點,但JSF AJAX /動作/監聽方法在語義上是錯誤地方做驗證。如果您在表單中輸入值錯誤,您實際上不希望在JSF生命週期中獲得如此深遠的意義。您希望JSF生命週期在JSF驗證階段後停止。
您想使用JSR303 Bean驗證註釋(@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驗證器。只需實現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
。另請參閱How to inject in @FacesValidator with @EJB, @PersistenceContext, @Inject, @Autowired。
感謝BalusC。在bean驗證執行其檢查或未指定訂單之前或之後調用此驗證程序? – 2011-05-05 15:26:34
在Bean驗證之前執行JSF驗證。但是,如果值爲'null'或空值,那麼不會執行除'required'以外的JSF驗證。所以如果你有'@ NotNull'沒有'required =「true」',那麼它將被首先執行。但是如果它不是空的,並且你有一個'@ Pattern',那麼JSF驗證器將首先被調用。 – BalusC 2011-05-05 15:47:18
你太棒了。但我可以不只是使用正常的jsf驗證器,但只驗證使用bean驗證屬性?如果它是有效的,那麼我可以檢查數據庫。我嘗試了Validator類的validateProperty方法,但是我不能只是讓它工作,因爲它要求組和其他語義我不明白 – 2011-05-05 18:34:17
爲此,最好使用action方法而不是actionListener。然後,如果用戶名存在,則可以從此方法返回null
(重新加載觸發該操作的頁面)。這裏有一個例子:
:
<h:commandButton action="#{testBean.doAction}" value="and... Action"/>
在bean
:
public String doAction() {
if (userExists) {
return null;
} else {
// go on processing ...
}
}
您可以定義在faces-config.xml文件中的導航情況。這將允許您根據bean的返回值將用戶重定向到給定頁面。
在下面的示例中,根據「myMethod()」的返回值,suer被重定向到兩頁中的一頁。
<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>
是的,你可以。您可以在動作監聽器方法中進行驗證,如果您的自定義驗證失敗,請添加面孔消息,然後在返回之前調用FacesContext.validationFailed()
。
該解決方案的唯一問題是,它發生在JSF驗證和bean驗證之後。即,在驗證階段之後。如果您有多個操作監聽器,請說listener1和listener2:如果您在listener1中的自定義驗證失敗,它將繼續執行listener2。但畢竟,你會在AJAX響應中得到validationFailed。
如果這是在actionListener中完成的話,那麼您將需要確定action方法檢查'facesContext.isValidationFailed()'繼續。 – Lucas 2013-01-24 16:54:39
我想你不應該重定向到另一個頁面,除非用戶名是有效的。 – Benchik 2011-05-05 06:04:21