如何在JSF中进行重定向
我有一个网络应用程序,用户可以直接发送到某些特定页面(例如他可以查看或编辑项目的页面)。 为实现这一目标,我们提供了一个特定的url。 这些URL位于当前Web应用程序之外(即,它们可以存在于另一个Web应用程序或电子邮件中)。
url看起来像http://myserver/my-app/forward.jsf?action=XXX¶m=YYY
,其中:
- action表示将重定向用户的页面。 您可以将此视为
faces-config.xml
中navigation-case
中任何JSF操作from-outcome
。 - actionParam是上一个操作的参数(通常是项ID)
例如,我可以拥有这些url:
-
http://myserver/my-app/forward.jsf?action=viewItem&actionParam=1234
-
http://myserver/my-app/forward.jsf?action=editItem&actionParam=1234
当然,我有一个Java类(bean),它将检查一些安全性约束(即用户是否允许查看/编辑相应的项?)然后将用户重定向到正确的页面(例如edit.xhtml
, view.xhtml
或access-denied.xhtml
)。
目前的实施
目前,我们有一个完成前进的基本方法。 当用户单击该链接时,将调用以下XHTML页面:
document.getElementById('forwardForm:forwardBtn').click();
如您所见,我在Java bean中绑定了两个组件。 它们将用于存储
action
和actionParam
请求参数的值(使用FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("actiontParam");
)。 我还提供了doForward
方法,该方法将在呈现页面时立即调用,这将(再次)将用户重定向到真实页面。 方法是:
public void doForward(ActionEvent evt) { FacesContext facesContext = FacesContext.getCurrentInstance(); String redirect = // define the navigation rule that must be used in order to redirect the user to the adequate page... NavigationHandler myNav = facesContext.getApplication().getNavigationHandler(); myNav.handleNavigation(facesContext, null, redirect); }
这个解决方案正在运行,但我有两个问题:
- 我不喜欢它的实现方式。 我确信我可以有一些更简单的东西(使用Servlet?)。
- 这个解决方案是使用Javascript,我不能使用Javascript(因为这个转发可能被旧的Blackberry用户使用,其中不支持Javascript)。
所以我的问题是如何重构这个重定向/转发function?
技术信息
Java 1.6,JSF 1.2,Facelets,Richfaces
在faces-config.xml
中将GET查询参数设置为托管属性,这样您就不需要手动收集它们:
forward com.example.ForwardBean request action #{param.action} actionParam #{param.actionParam}
这样,请求forward.jsf?action=outcome1&actionParam=123
将让JSF将action
和actionParam
参数设置为ForwardBean
action
和actionParam
属性。
创建一个小视图forward.xhtml
(这么小,以至于它适合默认的响应缓冲区(通常为2KB),以便导航处理程序可以重置它,否则你需要增加servletcontainer配置中的响应缓冲区),它会调用bean关于f:view
beforePhase
的方法:
ForwardBean
可能如下所示:
public class ForwardBean { private String action; private String actionParam; public void navigate(PhaseEvent event) { FacesContext facesContext = FacesContext.getCurrentInstance(); String outcome = action; // Do your thing? facesContext.getApplication().getNavigationHandler().handleNavigation(facesContext, null, outcome); } // Add/generate the usual boilerplate. }
navigation-rule
言自明(请注意
条目,它们将执行ExternalContext#redirect()
而不是ExternalContext#dispatch()
):
outcome1 /outcome1.xhtml outcome2 /outcome2.xhtml
另一种方法是使用forward.xhtml
作为
#{forward}
并更新要在@PostConstruct
上调用的navigate()
方法(将在bean构造和所有托管属性设置之后调用):
@PostConstruct public void navigate() { // ... }
它具有相同的效果,但视图方面并非真正自我记录。 所有它基本上都是打印ForwardBean#toString()
(并因此隐含地构造bean,如果还不存在)。
注意JSF2用户,有一种更简洁的方法,用
传递参数,以及通过
处理重定向/导航的更强大的方法。 另见:
- 点击bean方法并重定向GET请求
- 有没有简单的方法来预处理和重定向GET请求?
FacesContext context = FacesContext.getCurrentInstance(); HttpServletResponse response = (HttpServletResponse)context.getExternalContext().getResponse(); response.sendRedirect("somePage.jsp");
你应该使用action而不是actionListener:
并以密切的方式你正确的事情:
public String close() { return "index?faces-redirect=true"; }
index是你的一个页面(index.xhtml)
当然,所有这些工作人员都应该写在我们的原始页面中,而不是中间页面。 在close()
方法中,您可以使用参数动态选择重定向的位置。
编辑2
我终于通过实现我的前进动作找到了一个解决方案:
private void applyForward() { FacesContext facesContext = FacesContext.getCurrentInstance(); // Find where to redirect the user. String redirect = getTheFromOutCome(); // Change the Navigation context. NavigationHandler myNav = facesContext.getApplication().getNavigationHandler(); myNav.handleNavigation(facesContext, null, redirect); // Update the view root UIViewRoot vr = facesContext.getViewRoot(); if (vr != null) { // Get the URL where to redirect the user String url = facesContext.getExternalContext().getRequestContextPath(); url = url + "/" + vr.getViewId().replace(".xhtml", ".jsf"); Object obj = facesContext.getExternalContext().getResponse(); if (obj instanceof HttpServletResponse) { HttpServletResponse response = (HttpServletResponse) obj; try { // Redirect the user now. response.sendRedirect(response.encodeURL(url)); } catch (IOException e) { e.printStackTrace(); } } } }
它起作用(至少关于我的第一次测试),但我仍然不喜欢它的实现方式…更好的主意?
编辑此解决方案不起作用。 实际上,当doForward()
函数时,JSF生命周期已经启动,然后无法重新创建新请求。
解决这个问题的一个想法,但我真的不喜欢它,是在setBindedInputHidden()
方法之一强制执行doForward()
操作:
private boolean actionDefined = false; private boolean actionParamDefined = false; public void setHiddenActionParam(HtmlInputHidden hiddenActionParam) { this.hiddenActionParam = hiddenActionParam; String actionParam = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("actionParam"); this.hiddenActionParam.setValue(actionParam); actionParamDefined = true; forwardAction(); } public void setHiddenAction(HtmlInputHidden hiddenAction) { this.hiddenAction = hiddenAction; String action = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("action"); this.hiddenAction.setValue(action); actionDefined = true; forwardAction(); } private void forwardAction() { if (!actionDefined || !actionParamDefined) { // As one of the inputHidden was not binded yet, we do nothing... return; } // Now, both action and actionParam inputHidden are binded, we can execute the forward... doForward(null); }
此解决方案不涉及任何Javascript调用,并且工作不起作用。