JSF在调用action方法之前不更新值

时间:2013-10-27 04:24:47

标签: java jsf jsf-2

我正在使用JSF创建一个Timesheet应用程序,目前正在处理登录部分。这是一个直接登录,它获取登录ID和密码,并检查它是否是一个有效的组合(虽然此时我只是在管理员登录)。

我的问题似乎是JSF在调用verify操作方法之前没有更新我的loginID和密码字段,因为当我单击login时,它会抛出NullPointerException。这是我的验证方法。

/**
 * Verifies that the loginID and password is the super user combination.
 * @return true if it is, false if it is not.
 */
public String verifyUser() {
    ResourceBundle login = getSuperUserLogin();
    String user = getLoginID();
    String checkPassword = getPassword();
    if(!login.containsKey(user) || !checkPassword.equals(login.getString(user))) {
        return "regular";
    }
    return "super";
}

和我的登录页面

<body>
    <div class = "content" align = "center">
        <h:form>
            <h3>#{msgs.loginTitle}</h3>
            <h:panelGrid columns="2" cellpadding = "4">
                    <h:outputText value = "#{msgs.userID}" styleClass="title" />
                    <h:inputText value="#{user.loginID}" styleClass="rounded"  />
                    <h:outputText value = "#{msgs.password}" styleClass="title" />
                    <h:inputSecret value="#{user.password}" styleClass="rounded" />
             </h:panelGrid>
             <p><h:commandButton value="#{msgs.login}" styleClass="button" action="#{superUser.verifyUser}"/></p>
          </h:form>
      </div>

现在,如果我将登录ID和密码硬编码到用户和checkPassword字段中,那么它没有问题。所以我认为问题在于loginID和密码字段为空(因为getLoginID()和getPassword()只是返回这些字段的常规旧getter)。我知道答案是愚蠢的,我会因为错过而感到尴尬,但此时我正在撞墙。

信息完成后,loginID和密码字段位于User类中(提供了适当的setter),而verifyUser方法位于子类SuperUser中。我不知道为什么这一点很重要,但这将是我第一个使用超级和子类的JSF应用程序,所以你永远不会知道。

修改 根据要求,这是我的用户类代码

package ca.bcit.infosys.timesheet.model;
import java.util.ResourceBundle;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;

@Named
@ApplicationScoped
public class User implements java.io.Serializable {
/** The user's name */
private String name;
/** The user's employee number */
private int empNumber;
/** The user's login ID */
private String loginID;
/** The user's password */
private String password;
/** Is the user currently in an editable state (i.e. the user's information can be edited on the webpage) */
private boolean editable;
private ResourceBundle superUserLogin = ResourceBundle.getBundle("ca.bcit.infosys.timesheet.model.messages");

/**
 * @return the name
 */
public String getName() {
    return name;
}

/**
 * @param name the name to set
 */
public void setName(String name) {
    this.name = name;
}

/**
 * @return the empNumber
 */
public int getEmpNumber() {
    return empNumber;
}

/**
 * @param empNumber the empNumber to set
 */
public void setEmpNumber(int empNumber) {
    this.empNumber = empNumber;
}

/**
 * @return the loginID
 */
public String getLoginID() {
    return loginID;
}

/**
 * @param loginID the loginID to set
 */
public void setLoginID(String loginID) {
    this.loginID = loginID;
}

/**
 * @return the password
 */
public String getPassword() {
    return password;
}

/**
 * @param password the password to set
 */
public void setPassword(String password) {
    this.password = password;
}

/**
 * @return the isEditable
 */
public boolean isEditable() {
    return editable;
}

/**
 * @param isEditable the isEditable to set
 */
public void setEditable(boolean editable) {
    this.editable = editable;
}

/**
 * @return the Property containing the super user login information.
 */
public ResourceBundle getSuperUserLogin() {
    return superUserLogin;
}

}

1 个答案:

答案 0 :(得分:3)

您目前的方法存在很多误解。我建议首先学习并掌握JSF的基本概念来实现这一目标。

为了使这项工作,您需要一个只需要为服务器的每个请求活动的bean。该bean将负责从JSF表单获取数据并执行验证操作。然后,您只需将字段和登录方法绑定到JSF表单。

Java代码

//Looks like you use CDI (or probably you just think this is what you need)
@Named
//The bean just needs to be alive on each request
//Since you're using CDI, make sure this annotation comes from
//javax.enterprise.context package
@RequestScoped
//Usually, the managed beans used in JSF are named with "Bean" suffix
//Since it is for learning purposes, I'll name this bean UserBean
public class UserBean {
    //fields to hold username and password
    //they both are Strings
    private String username;
    private String password;
    //getters and setters methods
    //I won't add them since it is boilerplate code for this sample
    //...

    //Method to handle your login action
    //If successful login, then returns the name of the next view
    //If unsuccessful, returns null to stay in the current view
    public String login() {
        //Sample implementation, remember to change it for A REAL ONE
        if ("admin".equals(username) &&
            "adminPass".equals(password)) {
            //Successful login
            //Make sure to have a file index.xhtml in the same folder
            //where the login JSF page resides (for sample purposes)
            return "index";
        }
        //Unsuccessful login
        //Also, it is a good idea to show a message for end user
        FacesMessage message = new FacesMessage("Invalid login.");
        FacesContext.getCurrentInstance().addMessage(null, message);
        return null;
    }
}

JSF代码

<h:form>
    <h3>Login Title</h3>
    <h:panelGrid columns="2">
        <h:outputText value="User Id" />
        <h:inputText value="#{userBean.username}" />
        <h:outputText value="Password" />
        <h:inputSecret value="#{userBean.password}" />
    </h:panelGrid>
    <h:commandButton value="Login" action="#{userBean.login}" />
    <br />
    <h:messages />
</h:form>

在开始之前,您需要了解的一些事情:

  • JSF beans 与CDI bean不一样。这在这里有更好的解释:Backing beans (@ManagedBean) or CDI Beans (@Named)?。我建议阅读Bozho和BalusC的答案。
  • 定义托管bean的范围。 JSF初学者面临的主要问题之一是为bean选择合适的范围。例如,您开始将bean标记为@ApplicationScoped,这意味着整个应用程序有一个用户bean,这意味着两个不同的最终用户将具有相同的用户对象(现在你开始担心)。您可以在此处了解有关此内容的更多信息:How to choose the right bean scope?
  • 正如您从上面的链接中看到的那样,有一个视图范围。如果您正在使用Java EE 7,那么您将发现此范围也适用于您的CDI bean。如果您正在使用Java EE 6,那么您将发现此处没有注释。但请不要担心,OmniFaces从版本1.6开始就使用org.omnifaces.cdi包中的自定义@ViewScoped注释来解决此问题。
  • 由于您从JSF / CDI开始,我建议每个视图使用一个bean。如果您有更多经验,可以在同一视图中使用两个或更多个bean。这是一个很好的例子:Can I use multiple managed bean in the same xhtml page?