在另一个页面中打印bean数据(RequestScope)

时间:2013-02-23 06:35:01

标签: java jsf jsf-2 scope javabeans

编辑4

我想要做的是实施 forgotPassword 页面。例如,我已经采用了以下示例,这不是真正的用户相关问题,我将在会话范围内保留用户名。

index.xhtml将忘记密码页面,我将输入用户名。输入用户名后,我会点击Welcome Me - Action,在chkMe(),我会检查该用户并在他/她的电子邮件ID和welcome.xhtml上发送新密码,我会说{{1 }}


主要职位

我试图用两种情况将数据从一个bean打印到另一个bean。以下是我的代码。

的index.xhtml

Hi User ABC, we have sent new password at asdfasdf@dasf.com

welcome.xhtml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:f="http://java.sun.com/jsf/core"      
      xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>JSF 2.0 Hello World</title>
    </h:head>
    <h:body>
        <h3>JSF 2.0 Hello World Example - hello.xhtml</h3>
        <h:form>
           <h:inputText value="#{helloBean.name}"></h:inputText>
           <h:commandButton value="Welcome Me - Plain" action="welcome"></h:commandButton>
           <h:commandButton value="Welcome Me - Action" action="#{helloBean.chkMe()}"></h:commandButton>
        </h:form>
    </h:body>
</html>

HelloBean.java

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"    
      xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>JSF 2.0 Hello World</title>
    </h:head>
    <h:body bgcolor="white">
        <h3>JSF 2.0 Hello World Example - welcome.xhtml</h3>
        <h4>Welcome --#{helloBean.name}--</h4>
    </h:body>
</html>

当我在文本字段中输入文本import javax.faces.bean.ManagedBean; import javax.faces.bean.SessionScoped; import java.io.Serializable; import javax.faces.bean.RequestScoped; @ManagedBean @RequestScoped public class HelloBean implements Serializable { private static final long serialVersionUID = 1L; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public String chkMe() { return takeMeToAnotherPage("welcome"); } public String takeMeToAnotherPage(String linkToGo) { return linkToGo + "?faces-redirect=true"; } } 并单击按钮Checking时,我在welcome.xhtml中看到文本为Welcome Me - Plain文本,但是当我点击Welcome --Checking--时,我没有看到任何文字(我看作Welcome Me - Action

我不知道为什么会这样。

任何想法/建议为什么会这样。


编辑1

我认为这是因为Welcome ----造成的,但我必须使用它,好像我不使用?faces-redirect=true,地址栏中的网址是以前的网址。

e.g。如果我在page1.xhtml上,我转到page2.xhtml,仍然会说page1.xhtml。

所以不确定在这种情况下该怎么做。


编辑2

嗯,我真正想做的是忘记密码页面,我将在index.xhtml中输入用户名(考虑上面的例子),如果该用户名是正确的,在welcome.xhtml上,我将?faces-redirect=true。< / p>

RequestScope 工作正常,但问题出在URL地址上,因此我添加了Hi User ABC, Please use new password for next login. We have sent you email at blah@blah.com。但是由于它的重定向,http会话正在关闭,因此在welcome.xhtml上,我没有得到任何价值(这就是上面发生的事情)。

?faces-redirect=true的另一个解决方案是使用FlashScope 但问题是当我刷新welcome.xhtml时,数据已消失 让我发疯。

有人可以建议您需要做什么吗?


编辑3

会话范围中的问题如下。

考虑我打开两个标签,在两个标签上我都有index.xhtml。在tab1上,我输入skuntsel,然后点击Fahim。在tab1上,welcome.xhtml出现,我看到文本为Welcome Me - Action这很完美。

现在我来到tab2,输入名称Welcome Fahim,然后点击XYZ我得到welcome.xhtml,我看到文字为Welcome Me - Action这也很完美。

问题是当我回到tab1并刷新页面时。当我刷新tab1(welcome.xhtml)时,我看到Welcome XYZ错误,因为它早于Welcome XYZ,它应该是Welcome Fahim

8 个答案:

答案 0 :(得分:2)

根据我的口味,在会话范围内使用当前用户是一个好主意。

但是,如果它不适合你,我可以提供更多选择。

将用户名作为视图参数传递

转换为

<h:form>
    <h:inputText value="#{helloBean.name}"/>
    <h:commandButton value="Welcome Me - Action" action="#{helloBean.chkMe}"/>
</h:form>

public String chkMe() {
    return takeMeToAnotherPage("welcome");
}

public String takeMeToAnotherPage(String linkToGo) {
    return linkToGo + "?faces-redirect=true&username=" + name;
}

以及welcome.xhtml

中的其他视图参数
<f:metadata>
    <f:viewParam name="username"/>
</f:metadata>

另一种选择是及时实例化另一个请求范围的bean 并将信息传递给它

<h:form>
   <h:inputText value="#{helloBean.name}"/>
   <h:commandButton value="Welcome Me - Plain" action="welcome">
       <f:setPropertyActionListener value="#{helloBean.name}" target="#{welcomePageBean.username}"/>
   </h:commandButton>
</h:form>

@ManagedBean
@RequestScoped
WelcomePageBean {

    private String username;//+getter + setter
    //other fields associated with the welcome view

}

使用Flash对象

详细信息输入视图(片段),base.xhtml

<h:form>
    <h:outputText value="Enter user name for password reset: " />
    <h:inputText value="#{flash.username}" />
    <br/>
    <h:commandButton value="Send me a confirmation email" action="#{forgotBean.changePassword}" />
<h:form>
ForgotBean

base.xhtml

@ManagedBean
@RequestScoped
public class ForgotBean {

    public ForgotBean() {   }

    public String changePassword() {
        //check user constraints and return failure outcome in case somthing is wrong
        //generate new password and persist it to the database
        //send a configmation e-mail
        return "successful-reset?faces-redirect=true";
    }

}

成功观点(片段),successful-reset.xhtml

<h:outputText value="Password was reset for user #{receptorBean.username}, e-mail configmation sent." />
<br/>
<h:link value="View homepage" outcome="home" />
ReceptorBean

successful-reset.xhtml

@ManagedBean
@RequestScoped
public class ReceptorBean {

    @ManagedProperty("#{flash}")
    private Flash flash;

    private String username;

    public ReceptorBean() {   }

    public String getUsername() {
        if(username == null) {
            String uname = (String)flash.get("username");
            flash.keep("inputText");
            username= uname;
        }
        return username;
    }

    public Flash getFlash() {
        return flash;
    }

    public void setFlash(Flash flash) {
        this.flash = flash;
    }

}

答案 1 :(得分:1)

我认为faces-redirect = true会产生一个http重定向,一个http转发。重定向由浏览器处理,浏览器发送新的http请求。由于您正在使用RequestScoped Mbean,因此将使用新的MBean呈现新请求。 因此,使用SessionScoped Mbean或在不重定向的情况下导航。

答案 2 :(得分:1)

因此,你的bean可能是@RequestScoped。将其更改为Session scoped bean。

答案 3 :(得分:1)

将此代码添加到body标记

下面的索引页面
<t:saveState value="#{helloBean}" />

for t tag lib use

<%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>

并添加tomahawk lib。

如果您不了解savestate或param传递,请参阅this link

这将帮助您传递值的基本步骤。在jsf中的页面之间。

答案 4 :(得分:1)

使用Flash对象的示例

我实际上并不了解什么不起作用。下面是main - dependent视图的工作示例,其中第一个视图上的命令按钮触发重定向到从属视图,并使用JSF Flash对象处理数据传输。在提供的答案中,第二页数据(username在回发和页面刷新操作中幸存下来。

我将再次重新发布代码。

主要观点:

<h:form>
    <h:outputText value="Enter your name: " />
    <h:inputText value="#{flash.username}" />
    <h:commandButton value="Reset my password" action="#{forgotBean.changePassword}" />
</h:form>

忘了豆:

@ManagedBean
@RequestScoped
public class ForgotBean {

    public ForgotBean() {   }

    public String changePassword() {
        return "/dependent?faces-redirect=true";
    }

}

依赖观点:

<h:form>
   <h:outputText value="#{confirmBean.username}: your password has been changed and the confirmation has been sent to [get value from your bean]" />
   <h:commandButton value="Postback" />
</h:form>

确认bean:

@ManagedBean
@RequestScoped
public class ConfirmBean {

    @ManagedProperty("#{flash}")
    private Flash flash;

    private String username;

    public ReceivedBean() {   }

    public String getUsername() {
        if(username == null) {
            String uname = (String)flash.get("username");
            flash.keep("username");
            username = uname;
        }
        return username;
    }

    public Flash getFlash() {
        return flash;
    }

    public void setFlash(Flash flash) {
        this.flash = flash;
    }

}

或者,您也可以在@PostConstruct ConfirmBean中进行预处理(验证用户名,收集电子邮件等数据)。此外,您可以@ViewScoped执行此操作,以便在回发时不会进行预处理。

@PostConstruct方法的启动示例:

@PostConstruct
public void init() {
    //without managed property flash object is also available via
    //FacesContext.getCurrentInstance().getExternalContext().getFlash()
    String name = flash.get("username");
    flash.keep("username");
    //do necessary validations
    //get necessary data from your service
    //handle wrong user input
    this.username = name;
    //set up other data of the bean
    //change setter and getter of the field username to 'ordinary'
}

使用会话范围bean的示例

如果您希望bean能够在页面刷新后生存(因此,@ViewScoped失败),如果您想实现Post-Redirect-Get(因此,<f:setPropertyActionListener>失败),如果您想要工作on 基本上相同的数据,即虚拟未定义,在不同的标签中(因此,Flash失败),如果你想要阻止有意义的获取请求(因此,<f:viewParam>失败了),然后我看到的唯一方法( 保留在JSF 中)就是使用@SessionScoped {{1}这将使用户输入数据保存在集合中。这样,页面刷新回发重定向多标签有意义的视图参数限制已放宽,将在您的应用程序中启用。

请注意,提供的代码不会考虑任何异常处理/检查。而且,更重要和相关的是,作为地图中的关键字的递增整数应该更好地替换随机值,如UUID

视图(m代表索引,d代表欢迎):

@ManagedBean):

m.xhtml

<h:body> <h:form> <h:inputText value="#{forgotBean2.username}" /> <h:commandButton value="Change my password" action="#{forgotBean2.changePassword}" /> </h:form> </h:body> ):

d.xhtml

bean(<f:metadata> <f:viewParam name="id" required="true" /> </f:metadata> <h:body> <h:form> <h:outputText value="#{confirmBean2.username}: your password has been changed" /> <h:commandButton value="Postback" action ="#{confirmBean2.postbackAction}" /> </h:form> </h:body> - 用于ForgotBean2m.xhtml中的商家操作 - 用于ConfirmBean2d.xhtml中的展示和(可能)操作 - 用于存储会议中的信息):

UserRequestsBean):

ForgotBean2

@ManagedBean @RequestScoped public class ForgotBean2 { @ManagedProperty("#{userRequestsBean}") private UserRequestsBean userRequestsBean; private String username; public ForgotBean2() { } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public UserRequestsBean getUserRequestsBean() { return userRequestsBean; } public void setUserRequestsBean(UserRequestsBean userRequestsBean) { this.userRequestsBean = userRequestsBean; } public String changePassword() { //do business job Map<Integer, String> fMap = userRequestsBean.getRequests(); int id = 0; if(fMap.containsValue(username)) { for(Map.Entry<Integer, String> entry : fMap.entrySet()) { if(entry.getValue().equals(username)) { id = entry.getKey(); break; } } } else { if(fMap.isEmpty()) { id = 1; } else { id = (int)Collections.max(fMap.keySet()) + 1; } fMap.put(id, username); } return "/q15038451/d?faces-redirect=true&id=" + Integer.toString(id); } } ):

ConfirmBean2

@ManagedBean @ViewScoped public class ConfirmBean2 implements Serializable { @ManagedProperty("#{userRequestsBean}") private UserRequestsBean userRequestsBean; private Integer id; private String username; public ConfirmBean2() { } @PostConstruct public void init() { if(id == null) { String vpid = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("id"); id = Integer.parseInt(vpid); } Map<Integer, String> fMap = userRequestsBean.getRequests(); String uname = fMap.get(id); //do necessary validations //get necessary data from your service //handle wrong user input this.username = uname; //set up other data of the bean } public UserRequestsBean getUserRequestsBean() { return userRequestsBean; } public void setUserRequestsBean(UserRequestsBean userRequestsBean) { this.userRequestsBean = userRequestsBean; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String postbackAction() { return null; } } ):

UserRequestsBean

如上所述,您可能希望生成@ManagedBean @SessionScoped public class UserRequestsBean implements Serializable { private Map<Integer, String> requests = new HashMap<Integer, String>(); public UserRequestsBean() { } public Map<Integer, String> getRequests() { return requests; } public void setRequests(Map<Integer, String> requests) { this.requests = requests; } } s(提供唯一和随机值)而不是递增UUID

答案 5 :(得分:1)

让我们从JSF中摘要并分析您从HTTP级别获得的需求。

网址必须更改为welcome.xhtml

这意味着当单击按钮时,必须向服务器发送两个请求:

POST index.xhtml (with username parameter in the request body) - 将用户的输入提交给服务器

GET welcome.xhtml - 获取新页面

当服务器处理GET welcome.xhtml请求时,有两种方法可以获取数据:

  • 从客户端的请求中获取数据(只有通过GET请求传递数据的方法是将其放入URL)
  • 在服务器上提供它(我们应该将它保存在'POST index.xhtml'处理中)

页面必须经过刷新

刷新只是上一次请求的重复。 这意味着我们正在重复GET welcome.xhtml请求。 我们再次使用相同的两种方式来获取数据(来自url和服务器)。

网址不得包含数据(用户名)

这意味着无法从客户端的请求中检索数据。因此,获取数据的唯一方法是在“POST index.xhtml”处理期间首先将其存储在服务器上。

具有不同数据的多个选项卡

哎呀,迄今为止幸存的唯一解决方案无法满足最后的要求。 由于标签共享相同的浏览器会话(相同的cookie等),因此无法区分不同标签的请求。 因此,来自不同选项卡的请求看起来完全相同,无法确定用户刷新页面或复制网址并打开新选项卡。

结论

如果你要求严格要求 - 不可能实现你想要的(它不是JSF的限制,它是HTTP的工作方式)。所以你必须放松要求。

删除多个标签支持 - 您可以使用会话范围。

删除生存刷新 - 您可以使用Flash范围。


最有趣的是放宽了不在url中传递数据的要求。

最简单的方法是按原样传递username参数。您可能会说这是一个安全风险,但我会说风险与在POST index.xhtml请求中传递用户名非常相似(只是传递参数的方式有点不同)。我看到的另一个安全风险是,有人可以通过用户的计算机和浏览器的网址栏中的pry用户名。

更高级的方法是加密用户名并在URL中传递加密值,并在处理welcome.xhtml时对其进行解密。

如果我们进一步开发这种方法,我们将接近所谓的对话范围(查看JBoss Seam长时间运行的对话)。当您添加到URL无意义(从用户角度来看)参数时,参数的值是一些标识符,用作服务器上数据映射的键。

对话范围的虚拟实现:

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

@ManagedBean
@SessionScoped
public class Bean implements Serializable {

    private int prevCid = 0;

    private Map<String, Object> conversationScope = new HashMap<String, Object>();

    public String getName() {
        return (String) getData();
    }

    public void setName(String name) {
        setData(name);
    }

    public String chkMe() {
        return takeMeToAnotherPage("welcome");
    }

    public String takeMeToAnotherPage(String linkToGo) {
        return linkToGo + "?cid=" + getCid() + "&faces-redirect=true";
    }

    private Object getData() {
        return conversationScope.get(getCid());
    }

    private void setData(Object o) {
        conversationScope.put(getCid(), o);
    }

    private String getCid() {
        HttpServletRequest req = (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
        String cid = (String) req.getAttribute("cid");

        if (cid == null) {
            cid = req.getParameter("cid");
            if (cid == null) {
              cid = "" + prevCid++;
            }
            req.setAttribute("cid", cid);
        }

        return cid;
    }
}

答案 6 :(得分:1)

刷新页面时,您正在向服务器提交新请求。由于HTTP协议是无状态的,您可以通过在URL中添加它来告诉服务器我是'user1',因为URL在选项卡中是唯一的。

如果您不想在URL中添加参数,服务器将使用会话维护两个请求之间的关系。然后,您可以使用会话来存储变量等。

在你的情况下它不起作用,因为你不想在标签之间共享数据。 选项卡之间共享数据的原因是因为会话cookie在选项卡上共享。

您需要禁用Cookie并使用URL重写来维护会话。

http://docstore.mik.ua/orelly/java-ent/servlet/ch07_03.htm

您正在使用的应用程序服务器应该在jsf中保留URL并附加了sessionid,但是在重定向时您可能需要将会话ID附加到您的URL。

    public String takeMeToAnotherPage(String linkToGo) {
           HttpServletRequest req = (HttpServletRequest)FacesContext.getCurrentInstance().
getExternalContext().getRequest(); 
           return linkToGo + "?faces-redirect=true&jsessionid=" 
+ req.getSession().getId();
        }

由于您的应用程序在多个选项卡上使用,您可以禁用会话cookie并使用URL重写来维护会话。

http://jf.omnis.ch/archives/2004/12/disabling-session-cookie-in-tomcat.html

https://community.jboss.org/thread/141685

另一种选择是使用会话范围,当用户访问欢迎页面时,您可以开始新的会话。您的应用程序将在两个选项卡中维护两个不同的对话。您可以在此处了解有关它的更多信息,

http://www.andygibson.net/blog/tutorial/cdi-conversations-part-2/

答案 7 :(得分:1)

刷新页面时,您正在向服务器提交新请求。由于HTTP协议是无状态的,您可以通过在URL中添加它来告诉服务器我是'user1',因为URL在选项卡中是唯一的。

如果您不想在URL中添加参数,服务器将使用会话维护两个请求之间的关系。然后,您可以使用会话来存储变量等。

在你的情况下它不起作用,因为你不想在标签之间共享数据。选项卡之间共享数据的原因是因为会话cookie在选项卡上共享。

您需要禁用Cookie并使用URL重写来维护会话。

http://docstore.mik.ua/orelly/java-ent/servlet/ch07_03.htm

您正在使用的应用程序服务器应该在jsf中保留URL并附加了sessionid,但是在重定向时您可能需要将会话ID附加到您的URL。

  public String takeMeToAnotherPage(String linkToGo) {
           HttpServletRequest req = (HttpServletRequest)FacesContext.getCurrentInstance().
getExternalContext().getRequest(); 
           return linkToGo + "?faces-redirect=true&jsessionid=" 
+ req.getSession().getId();
        }

由于您的应用程序在多个选项卡上使用,您可以禁用会话cookie并使用URL重写来维护会话。

http://jf.omnis.ch/archives/2004/12/disabling-session-cookie-in-tomcat.html

https://community.jboss.org/thread/141685

另一种选择是使用会话范围,当用户访问欢迎页面时,您可以开始新的会话。您的应用程序将在两个选项卡中维护两个不同的对话。您可以在此处了解有关它的更多信息,

http://www.andygibson.net/blog/tutorial/cdi-conversations-part-2/