我想要做的是实施 forgotPassword 页面。例如,我已经采用了以下示例,这不是真正的用户相关问题,我将在会话范围内保留用户名。
index.xhtml
将忘记密码页面,我将输入用户名。输入用户名后,我会点击Welcome Me - Action
,在chkMe()
,我会检查该用户并在他/她的电子邮件ID和welcome.xhtml上发送新密码,我会说{{1 }}
我试图用两种情况将数据从一个bean打印到另一个bean。以下是我的代码。
Hi User ABC, we have sent new password at asdfasdf@dasf.com
<?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>
<?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
)
我不知道为什么会这样。
任何想法/建议为什么会这样。
我认为这是因为Welcome ----
造成的,但我必须使用它,好像我不使用?faces-redirect=true
,地址栏中的网址是以前的网址。
e.g。如果我在page1.xhtml上,我转到page2.xhtml,仍然会说page1.xhtml。
所以不确定在这种情况下该怎么做。
嗯,我真正想做的是忘记密码页面,我将在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时,数据已消失 , 让我发疯。
有人可以建议您需要做什么吗?
考虑我打开两个标签,在两个标签上我都有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
。
答案 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>
<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
}
详细信息输入视图(片段),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)
我实际上并不了解什么不起作用。下面是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能够在页面刷新后生存(因此,@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>
- 用于ForgotBean2
,m.xhtml
中的商家操作 - 用于ConfirmBean2
,d.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级别获得的需求。
这意味着当单击按钮时,必须向服务器发送两个请求:
POST index.xhtml (with username parameter in the request body)
- 将用户的输入提交给服务器
GET welcome.xhtml
- 获取新页面
当服务器处理GET welcome.xhtml
请求时,有两种方法可以获取数据:
刷新只是上一次请求的重复。
这意味着我们正在重复GET welcome.xhtml
请求。
我们再次使用相同的两种方式来获取数据(来自url和服务器)。
这意味着无法从客户端的请求中检索数据。因此,获取数据的唯一方法是在“POST index.xhtml”处理期间首先将其存储在服务器上。
如果你要求严格要求 - 不可能实现你想要的(它不是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/