私のアプリケーションではBean検証がうまく機能しています。ここで、新しいユーザーがすでに選択されているユーザー名を選択していないことを確認したいと思います。
actionlistenerにはデータベースをチェックするコードがありますが、既存のユーザー名を選択した場合、ユーザーを強制的に元のページに戻すにはどうすればよいですか?
私のアプリケーションではBean検証がうまく機能しています。ここで、新しいユーザーがすでに選択されているユーザー名を選択していないことを確認したいと思います。
actionlistenerにはデータベースをチェックするコードがありますが、既存のユーザー名を選択した場合、ユーザーを強制的に元のページに戻すにはどうすればよいですか?
あなたはそれを行うことができますが、JSF ajax / action/listenerメソッドは意味的に検証を行うのに間違った場所です。フォームへの入力値が間違っている場合、実際にはJSFライフサイクルでそれほど遠くまで行きたくありません。JSF検証フェーズの後でJSFライフサイクルを停止する必要があります。
JSR303 Bean Validationアノテーション(@NotNull
およびその仲間)や制約バリデーターを使用するか、代わりにJSF Validator
(required="true"
、<f:validateXxx>
など)を使用します。これは、JSF検証フェーズ中に適切に呼び出されます。このように、検証が失敗した場合、モデル値は更新されず、ビジネスアクションは呼び出されず、同じページ/ビューに留まります。
特定の入力値がデータベースに従って一意であるかどうかを確認するための標準のBeanValidationアノテーションまたはJSFValidatorがないため、そのためのカスタムバリデーターを自家栽培する必要があります。
どちらの方法でも、ユーザー名の一意性をチェックするカスタムバリデーターを作成する方法を示します。
まず、カスタム@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
またはCDI 1.1以降でのみ動作するため、CDI 1.0を使用して@Inject
いるConstraintValidator
場合は、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応答でvalidationFailedを取得します。
この目的には、actionListenerではなくactionメソッドを使用することをお勧めします。次に、ユーザー名が存在する場合は、このメソッドから戻るnull
(アクションをトリガーしたページを再ロードする)ことができます。次に例を示します。
フェイスレット内:
<h:commandButton action="#{testBean.doAction}" value="and... Action"/>
Beanで:
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の戻り値に応じて、ユーザーを特定のページにリダイレクトできます。
以下の例では、suerは「myMethod()」の戻り値に応じて2つのページのいずれかにリダイレクトされます。
<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>