Вопрос по java, composite-component, validation, jsf, jsf-2 – Проверка перекрестного поля JSF через postValidate без поиска компонентов по имени в компоненте поддержки

2

Я создаю составной компонент формы входа. На странице, которая его использует, пройдет обработчик событий, который проверит имя пользователя и пароль. Обычно (без использования составных компонентов), когда мы выполняем перекрестную проверку поля черезpostValidateобработчик событий должен искать поля & apos; Компоненты по имени. Для валидатора было бы предпочтительнее этого не делать, потому что это внутренние детали компонента, которые следует абстрагировать.

Любая идея, как я могу получить преобразованные значения полей имени пользователя и пароля вpostValidate обработчик, не зная внутренних деталей составного компонента?

Update: Суть этого не в том, чтобы вообще не искать компоненты по имени, а в том, чтобы иметь возможность проходить перекрестную проверку полей составного компонента таким образом, чтобы не требовалось, чтобы использующая страница и / или бин знали внутреннее детали компонента.

Ваш Ответ

3   ответа
3

postValidate событие в составном компоненте иpostValidate метод в компоненте поддержки. Обратите внимание, как это решаетMethodExpression атрибут и вызывает переданный метод.

Вот составной компонент:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:cc="http://java.sun.com/jsf/composite"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:p="http://primefaces.org/ui">

    <!-- Login form. -->
    <cc:interface componentType="com.example.LoginForm">
        <cc:attribute name="emailAddress" type="java.lang.String" required="true"/>
        <cc:attribute name="rememberMe" type="java.lang.Boolean" required="true"/>
        <cc:attribute name="checkCredentials"
                      method-signature="void checkCredentials(java.lang.String,java.lang.String,java.lang.String)"
                      shortDescription="Parameters are clientId, username and password. If credentials are invalid, attach a FacesMessage to the component specified by clientId."
                      required="true"/>
        <cc:attribute name="actionListener" method-signature="void actionListener()" required="true"/>
        <cc:attribute name="registerOutcome" type="java.lang.String" required="true"/>
        <cc:attribute name="recoverPasswordOutcome" type="java.lang.String" required="true"/>
        <cc:attribute name="headerTitle" type="java.lang.String" default="Sign In"/>
        <cc:attribute name="emailAddressLabel" type="java.lang.String" default="Email address:"/>
        <cc:attribute name="passwordLabel" type="java.lang.String" default="Password:"/>
        <cc:attribute name="rememberMeLabel" type="java.lang.String" default="Stay signed in on this machine"/>
        <cc:attribute name="loginLabel" type="java.lang.String" default="Sign In"/>
        <cc:attribute name="recoverPasswordLabel" type="java.lang.String" default="Forgot password?"/>
        <cc:attribute name="emailAddressRequiredMessage" type="java.lang.String" default="Email address required"/>
        <cc:attribute name="passwordRequiredMessage" type="java.lang.String" default="Password required"/>
        <cc:attribute name="registerLabel" type="java.lang.String" default="Register"/>
    </cc:interface>

    <cc:implementation>
        <h:outputStylesheet library="components/example/login-form" name="style.css"/>

        <div id="#{cc.clientId}">
            <h:form id="form">

                <f:event type="postValidate" listener="#{cc.postValidate}"/>

                <div style="margin-top:10px;">
                    <p:panel header="#{cc.attrs.headerTitle}" styleClass="loginPanel">
                        <div class="login-form_errorContainer">
                            <p:messages rendered="#{facesContext.maximumSeverity.ordinal ge 2}"/>
                        </div>
                        <h:panelGrid columns="3">
                            <h:outputText styleClass="login-form_label" value="#{cc.attrs.emailAddressLabel}"/>
                            <h:panelGroup styleClass="login-form_cell">
                                <h:inputText id="emailAddress"
                                             value="#{cc.attrs.emailAddress}"
                                             required="true"
                                             requiredMessage="#{cc.attrs.emailAddressRequiredMessage}"
                                             styleClass="login-form_field"
                                             immediate="true"/>
                            </h:panelGroup>
                            <h:panelGroup/>

                            <h:outputText styleClass="login-form_label" value="#{cc.attrs.passwordLabel}"/>
                            <h:panelGroup styleClass="login-form_cell">
                                <h:inputSecret id="password"
                                               value="#{cc.attrs.password}"
                                               required="true"
                                               requiredMessage="#{cc.attrs.passwordRequiredMessage}"
                                               styleClass="login-form_field"
                                               immediate="true"/>
                            </h:panelGroup>
                            <h:link styleClass="login-form_link" value="#{cc.attrs.recoverPasswordLabel}" outcome="#{cc.attrs.recoverPasswordOutcome}"/>

                            <h:panelGroup/>
                            <p:selectBooleanCheckbox value="#{cc.attrs.rememberMe}" itemLabel="#{cc.attrs.rememberMeLabel}" immediate="true"/>
                            <h:panelGroup/>

                            <h:panelGroup/>
                            <h:panelGroup>
                                <p:commandButton id="submitForm" value="#{cc.attrs.loginLabel}" actionListener="#{cc.attrs.actionListener}" update="form"/>
                                <span class="login-form_or">or</span>
                                <h:link styleClass="login-form_link" value="#{cc.attrs.registerLabel}" outcome="#{cc.attrs.registerOutcome}"/>
                            </h:panelGroup>
                            <h:panelGroup/>
                        </h:panelGrid>
                    </p:panel>
                </div>
            </h:form>
        </div>
    </cc:implementation>
</html>

Резервный компонент:

@FacesComponent("com.example.LoginForm")
public class LoginFormComponent extends UIInput implements NamingContainer
{
    @Override
    protected Object getConvertedValue(FacesContext context, Object newSubmittedValue) throws ConverterException
    {
        UIInput emailAddressComponent = (UIInput) findComponent(EMAIL_ADDRESS_ID);
        UIInput passwordComponent = (UIInput) findComponent(PASSWORD_ID);
        String emailAddress = (String) emailAddressComponent.getValue();
        String password = (String) passwordComponent.getValue();
        return new LoginFormValue(emailAddress, password);
    }

    public void postValidate(ComponentSystemEvent e) {
        FacesContext ctx = getFacesContext();

        // Don't validate credentials if the username and/or password fields are invalid.
        if (!ctx.getMessageList(EMAIL_ADDRESS_ID).isEmpty() || !ctx.getMessageList(PASSWORD_ID).isEmpty())
        {
            return;
        }

        LoginFormValue value = (LoginFormValue) getConvertedValue(null, null);
        MethodExpression checkCredentials = (MethodExpression) getAttributes().get(CHECK_CREDENTIALS_ATTRIBUTE_NAME);
        checkCredentials.invoke(ctx.getELContext(), new Object[]{getClientId(), value.getEmailAddress(), value.getPassword()});
    }

    @Override
    public String getFamily()
    {
        return "javax.faces.NamingContainer";
    }

    public static final String CHECK_CREDENTIALS_ATTRIBUTE_NAME = "checkCredentials";
    public static final String EMAIL_ADDRESS_ID = "form:emailAddress";
    public static final String PASSWORD_ID = "form:password";
}

LoginFormValue класс для полноты:

public class LoginFormValue
{
    public LoginFormValue(String emailAddress, String password)
    {
        this.emailAddress = emailAddress;
        this.password = password;
    }

    public String getEmailAddress()
    {
        return emailAddress;
    }

    public String getPassword()
    {
        return password;
    }

    private String emailAddress;
    private String password;
}

Страница, которая использует форму входа:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ex="http://java.sun.com/jsf/composite/components/example">
    <h:head>
        <title></title>
    </h:head>
    <h:body>
        <ui:composition template="/WEB-INF/templates/myLayout.xhtml">
            <ui:define name="windowTitle">Sign In</ui:define>
            <ui:define name="body">

                <ex:login-form emailAddress="#{loginBean.emailAddress}"
                               rememberMe="#{loginBean.rememberMe}"
                               checkCredentials="#{loginBean.checkCredentials}"
                               actionListener="#{loginBean.submit()}"
                               recoverPasswordOutcome="recover-password"
                               registerOutcome="signup"/>

            </ui:define>
        </ui:composition>
    </h:body>
</html>

И наконец, компонент поддержки страницы:

@Named
@RequestScoped
public class LoginBean implements Serializable
{
    public String getEmailAddress()
    {
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress)
    {
        this.emailAddress = emailAddress;
    }

    public boolean isRememberMe()
    {
        return rememberMe;
    }

    public void setRememberMe(boolean rememberMe)
    {
        this.rememberMe = rememberMe;
    }

    /** Action listener for login-form. Called after validation passes. */
    public void submit()
    {
        User user = userDao.findByEmailAddress(emailAddress);
        userRequestBean.login(user.getUserId());

        // Remember me
        if (!rememberMe)
        {
            return;
        }

        // Handle rememberMe here (create a cookie, etc.)
    }

    /** Called by the backing component's postValidate event handler */
    public void checkCredentials(String clientId, String emailAddress, String password)
    {
        if (!securityEjb.checkCredentials(emailAddress, password))
        {
            FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Incorrect email address/password", null);
            FacesContext ctx = FacesContext.getCurrentInstance();
            ctx.addMessage(clientId, message);
            ctx.renderResponse();
        }
    }

    private String emailAddress = "";

    private boolean rememberMe = true;

    @Inject
    private UserRequestBean userRequestBean;

    @EJB
    private SecurityEjb securityEjb;

    @EJB
    private UserDao userDao;

    @EJB
    private LoginCookieDao loginCookieDao;
}
postValidate Метод-обработчик находится внутри компонента поддержки, а не компонента поддержки страницы, на которой он используется. Так что это приятно спрятано и скрыто из виду. Я мог бы упаковать компонент в банку, если бы захотел. Метод, который должен быть реализован,void checkCredentials(String,String,String) определяется в интерфейсе составного компонента, поэтому не требуется никаких внутренних знаний о компоненте. Distortum
Идентификаторы компонентов все еще жестко закодированы вpostValidate  метод обработчика. Итак, вы говорите, что проблема не в жестко закодированных идентификаторах, а в том, на какой класс они ссылаются?
Отредактировал вопрос, чтобы прояснить проблему. Distortum
0

JsfWarn для подобной проблемы, я думаю, что она решает вашу проблему намного чище.

В отличие от валидаторов JSF, WarningValidators выполняются до ответа-рендеринга после того, как модель была обновлена, приложение было вызвано, так что вы можете просто получить доступ к своему приложению для результатов проверки.

@Named
public class BarWarningValidator implements WarningValidator{

    @Inject
    private MyValidationBean validationBean;

    @Override
    public void process(FacesContext context, UIInput component, ValidationResult validationResult) {
        if(!validationBean.isBarValid()) {
            validationResult.setFacesMessage(new FacesMessage(FacesMessage.SEVERITY_WARN, "FooBar", "This is a warning."));
        }
    }
}

И добавьте валидатор в целевое поле:

<h:outputLabel for="bar" value="Default warning:" />
<h:inputText id="bar">
    <jw:warning validator="#{barWarningValidator}" />
    <f:ajax event="change" render="..." />
</h:inputText>
<h:message for="bar" />
0

f:event postValidate подход не оставляет вам много вариантов.

Опция, которую я предпочитаю, заключается в том, чтобы выполнить проверки последнего компонента формы, а затем передать другие компоненты с помощью привязки и атрибута f :.

Например

<h:inputText id="field1" binding="#{field1}" ... />
<h:inputText id="field2" validator="#{...}">
  <f:attribute name="field1" value="#{field1}"/>
</h:inputText>

Затем в вашем валидаторе вы можете извлечь другие компоненты из UIInput:

UIComponent field1 = field2.getAttributes().get("field1")
Хммм ... Это также нарушает абстракцию. Интересно, смогу ли я определить двухпараметрический (метод user / pass) атрибут метода в составном компоненте и каким-то образом вызвать его из события в базовом классе компонента. Distortum

Похожие вопросы