JSF bean action method return string test

2019-08-08 20:08发布

问题:

I tried to understand from this question the difference between returning null and "" in JSF action, but couldn't experience in real.

Below is the quote form the OP

From what I understand, when a JSF action returns "" (empty String) the user stays on the current page but the view is refreshed. However, when the action returns null the user still stays on the current page but the old view is reused

I am in a confusion of understanding the difference between returning "", null and "viewid?faces-redirect=true" from a JSF Backing bean. I tried to understand the difference with a small example, but I couldn't experience the actual difference. Below are the code snippets of my example.

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <display-name>Jsf Questions</display-name>
  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <context-param>
    <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
    <param-value>.jspx</param-value>
  </context-param>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>/jsfintroapp/*</url-pattern>
  </servlet-mapping>
</web-app>

faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee"
 xmlns:xi="http://www.w3.org/2001/XInclude"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd">
 <managed-bean>
  <managed-bean-name>userLogin</managed-bean-name>
  <managed-bean-class>com.srk.beans.UserLogin</managed-bean-class>
  <managed-bean-scope>request</managed-bean-scope>
 </managed-bean>
 <navigation-rule>
  <description>Contains list of all navigation rules here</description>
  <from-view-id>/*</from-view-id>
  <navigation-case>
   <display-name>WELCOME</display-name>
   <from-outcome>welcome</from-outcome>
   <to-view-id>/geek_examples/authorized_user.jspx</to-view-id>
  </navigation-case>
</faces-config>

My managed bean is a request scoped bean, as declared above.

UserLogin.java (Backing bean) code snippet

public String saveData() {
    //String returnString = "";//Case 1 : works - user stays on the same page
    //String returnString = null;//Case 2: works - user stays on the same page
    // Case 3:
    String viewId = FacesContext.getCurrentInstance().getViewRoot().getViewId();
    LOG.debug("view id = "+ viewId);
    String returnString = "?faces-redirect=true";//viewId+
    LOG.debug("return string = "+ returnString);
    LOG.debug("Username = "+ getName() + " password = "+ getPassword());
    return returnString;
}

login.jspx

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core">
<f:view>
<head>
<title>JSF Question</title>
</head>
<body>
    <h:form>
        <h3>User Name and Password</h3>
        <table>
            <tr>
              <td><h:outputLabel value="Name"/></td>
              <td><h:inputText value="#{userLogin.name}"/></td>
            </tr>
            <tr>
              <td><h:outputLabel value="Password"/></td>
              <td><h:inputText value="#{userLogin.password}"/></td>
            </tr>
            <tr>
              <td><h:commandButton value="Sign In" action="#{userLogin.saveData}"/></td>
            </tr>
        </table>
    </h:form>
</body>
</f:view>
</html>

Libraries used in this example

I tried to access the login.jspx page, entered values in the fields of form and clicked on Sign In. I can always see the same page, no matter what I return there in my saveData() method which is not showing any difference in behavior, Can somebody throw some light with respect to this concept?

回答1:

As answered in that answer, returning null or void will reuse the same JSF view, including all view scoped beans attached to it.

JSF 1.2, however, has no concept of a "view scoped bean". This was introduced in JSF 2.0. Moreover, you've a request scoped bean. It'll always be recreated on every request, regardless of how you navigate. So it's not really noticeable by looking at how the bean behaves. It's only noticeable if you manually mimic the JSF 2.0 view scope by putting an attribute in the UIViewRoot and then checking in the constructor of the request scoped bean if it's still present.

If you have used a JSF 2.x view scoped bean, you'd have noticed that it's recreated (i.e. it's (post)constructor is invoked again) when you navigate using a non-null outcome. See also a.o. How to choose the right bean scope?

And, the ?faces-redirect=true query string is also JSF 2.x specific and not recognized by JSF 1.x. In JSF 1.x, to achieve the same effect, either add <redirect/> to the <navigation-case>, or use ExternalContext#redirect().

public void saveData() throws IOException {
    // ...

    ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
    String url = ec.getRequestContextPath() + "/login.jsf";
    ec.redirect(url);
}

Which navigation approach to choose is elaborated in this related question: How to navigate in JSF? How to make URL reflect current page (and not previous one). Generally, in UX and SEO perspective, using POST for page-to-page navigation is bad practice. Always use GET for that or perform a redirect after POST.

In any case, when developing with JSF 1.x, you should not be looking at JSF 2.x targeted answers/resources. It'll only lead to confusion because many things are done differently (better!) in JSF 2.x. JSF 2.x exist more than 5 years already and JSF 1.x was EOL for nearly as long. It doesn't make sense to keep working on dead technology. Consider migrating.