如何在JSF中进行重定向

我有一个网络应用程序,用户可以直接发送到某些特定页面(例如他可以查看或编辑项目的页面)。 为实现这一目标,我们提供了一个特定的url。 这些URL位于当前Web应用程序之外(即,它们可以存在于另一个Web应用程序或电子邮件中)。

url看起来像http://myserver/my-app/forward.jsf?action=XXX&param=YYY ,其中:

  • action表示将重定向用户的页面。 您可以将此视为faces-config.xmlnavigation-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.xhtmlview.xhtmlaccess-denied.xhtml )。


目前的实施

目前,我们有一个完成前进的基本方法。 当用户单击该链接时,将调用以下XHTML页面:

        document.getElementById('forwardForm:forwardBtn').click();   

如您所见,我在Java bean中绑定了两个组件。 它们将用于存储actionactionParam请求参数的值(使用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将actionactionParam参数设置为ForwardBean actionactionParam属性。

创建一个小视图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调用,并且工作不起作用。