如何在 JSF 1.x 中对页面加载进行重定向
Posted
技术标签:
【中文标题】如何在 JSF 1.x 中对页面加载进行重定向【英文标题】:How to make a redirection on page load in JSF 1.x 【发布时间】:2011-05-01 06:36:55 【问题描述】:我有一个网络应用程序,可以将用户直接发送到某些特定页面(例如他可以查看或编辑项目的页面)。为此,我们提供了一个特定的网址。这些 url 位于当前网络应用程序外部(即它们可以存在于另一个网络应用程序或电子邮件中)。
网址看起来像http://myserver/my-app/forward.jsf?action=XXX&param=YYY
,其中:
navigation-case
中faces-config.xml
中任何JSF 操作的from-outcome
。
actionParam 是上一个操作的参数(通常是项目 ID)
例如,我可以拥有这些类型的网址:
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 页面:
<html>
<body id="forwardForm">
<h:inputHidden id="myAction" binding="#forwardBean.hiddenAction"/>
<h:inputHidden id="myParam" binding="#forwardBean.hiddenActionParam"/>
<h:commandButton id="forwardBtn" actionListener="#forwardBean.doForward" style="display: none;"/>
</body>
<script type="text/javascript">
document.getElementById('forwardForm:forwardBtn').click();
</script>
</html>
如您所见,我在我的 Java bean 中绑定了两个 <h:inputHidden>
组件。它们将用于存储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(因为此转发可能由不支持 Javascript 的老黑莓用户使用)。所以我的问题是如何重构这个重定向/转发功能?
技术资料
Java 1.6、JSF 1.2、Facelets、Richfaces
【问题讨论】:
我不确定是否最好这样做,但你给用户一个点击链接并使用中间 xhtml 并制作 javascript 点击,但你为什么不将它指向一个 servlet 和从那里你可以处理这种情况,我的意思是为什么不使用 servlet 而不是中间 xhtml。我认为在 jsf env 中添加我们自己的 servlet 并不坏 @org:这当然可以在没有 servlet 的情况下完成。使用 servlet 只会增加额外的维护麻烦,因为它无法即时访问导航案例。 @BalusC 谢谢,我想知道,怎么做? , 可以直接在xhtml上使用jsp scriptlets吗? @org:我有一个理论,但我只发布我可以根据自己的经验判断它是正确的答案。而且我现在手头没有 JSF 1.2 操场环境。也许如果我有更多的时间。完全不鼓励使用 scriptlet。无论何种视图技术。 @org scriptlet 超出了我的词汇范围 ;) 事实上,如果我使用 Servlet 创建解决方案,我将不会受益于 JSF 的导航案例。 【参考方案1】:假设 foo.jsp 是您的 jsp 文件。以下代码是您要重定向的按钮。
<h:commandButton value="Redirect" action="#trial.enter "/>
现在我们将检查在您的 java(服务)类中的定向方法
public String enter()
if (userName.equals("xyz") && password.equals("123"))
return "enter";
else
return null;
现在这是 faces-config.xml 文件的一部分
<managed-bean>
<managed-bean-name>'class_name'</managed-bean-name>
<managed-bean-class>'package_name'</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<navigation-case>
<from-outcome>enter</from-outcome>
<to-view-id>/foo.jsp</to-view-id>
<redirect />
</navigation-case>
【讨论】:
这并不能回答 OP 的真正要求。【参考方案2】:你应该使用action而不是actionListener:
<h:commandLink id="close" action="#bean.close" value="Close" immediate="true"
/>
并且在接近的方法中,您可以正确地使用以下内容:
public String close()
return "index?faces-redirect=true";
其中 index 是您的页面之一(index.xhtml)
当然,所有这些工作人员都应该写在我们的原始页面中,而不是中间页面。
在close()
方法中,您可以使用参数动态选择重定向的位置。
【讨论】:
使用action
或actionListener
对我当前的问题绝对没有影响。
OP 使用的是 JSF 1.x,而不是 2.x。另外,重定向会导致请求参数丢失。【参考方案3】:
将 GET 查询参数设置为 faces-config.xml
中的托管属性,这样您就不需要手动收集它们:
<managed-bean>
<managed-bean-name>forward</managed-bean-name>
<managed-bean-class>com.example.ForwardBean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>action</property-name>
<value>#param.action</value>
</managed-property>
<managed-property>
<property-name>actionParam</property-name>
<value>#param.actionParam</value>
</managed-property>
</managed-bean>
这样,请求forward.jsf?action=outcome1&actionParam=123
将让JSF 将action
和actionParam
参数设置为action
和actionParam
的ForwardBean
属性。
创建一个小视图forward.xhtml
(小到可以放入默认响应缓冲区(通常为 2KB),以便导航处理程序将其重置,否则您必须在 servletcontainer 的配置中增加响应缓冲区),这在f:view
的beforePhase
上调用一个bean 方法:
<!DOCTYPE html>
<html xmlns:f="http://java.sun.com/jsf/core">
<f:view beforePhase="#forward.navigate" />
</html>
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
不言自明(注意 <redirect />
条目会在封面下使用 ExternalContext#redirect()
而不是 ExternalContext#dispatch()
):
<navigation-rule>
<navigation-case>
<from-outcome>outcome1</from-outcome>
<to-view-id>/outcome1.xhtml</to-view-id>
<redirect />
</navigation-case>
<navigation-case>
<from-outcome>outcome2</from-outcome>
<to-view-id>/outcome2.xhtml</to-view-id>
<redirect />
</navigation-case>
</navigation-rule>
另一种方法是使用forward.xhtml
as
<!DOCTYPE html>
<html>#forward</html>
并更新要在@PostConstruct
上调用的navigate()
方法(将在bean 的构造和所有托管属性设置后调用):
@PostConstruct
public void navigate()
// ...
它具有相同的效果,但是视图端并不是真正的自我记录。它基本上所做的只是打印ForwardBean#toString()
(如果还没有,则隐式构造bean)。
请注意,对于 JSF2 用户,<f:viewParam>
传递参数的方式更简洁,<f:event type="preRenderView">
处理重定向/导航的方式更稳健。另见:
【讨论】:
我喜欢你使用<f:view beforePhase=#..."/>
的想法。但是,我的第一个测试没有成功,因为没有调用 beforePhase 方法。我不明白为什么......
我仍然不明白为什么第一个解决方案不起作用(也许我会为此创建一个单独的帖子),但第二个解决方案确实有效!非常感谢!
PhaseEvent
参数是否存在?究竟是什么 JSF impl/version?我必须承认我在 2.1-nightly 上测试了 faces-config 声明为 1.2(这样 2.x 功能将无法工作)。顺便说一句,该属性是在 1.2 中引入的(因此在 1.1 或更早版本上根本不起作用)。但@PostConstruct
也是如此。【参考方案4】:
编辑 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 调用,有效不有效。
【讨论】:
这确实很尴尬:)handleNavigation()
在这里完全没有必要(它又被response.sendRedirect()
覆盖)并且ExternalContext
有一个redirect()
方法。【参考方案5】:
FacesContext context = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse)context.getExternalContext().getResponse();
response.sendRedirect("somePage.jsp");
【讨论】:
不回答我的问题。你想让我把这段代码放在哪里?以及如何在没有任何用户交互或 Javascript 调用的情况下调用它?以上是关于如何在 JSF 1.x 中对页面加载进行重定向的主要内容,如果未能解决你的问题,请参考以下文章
JSF 如何从支持 bean 重定向到支持 bean 中动态构建的 URL?
h:commandbutton,如何重定向到外部站点?(JSF 2)[重复]