如何在JSF 1.x中对页面加载进行重定向

问题描述 投票:29回答:5

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

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

  • action表示用户将被重定向的页面。您可以将此视为from-outcomenavigation-case中任何JSF操作的faces-config.xml
  • 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.xhtmlview.xhtmlaccess-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>组件。它们将用于存储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)。

所以我的问题是如何重构这个重定向/转发功能?

技术信息

Java 1.6,JSF 1.2,Facelets,Richfaces

java jsf jsf-1.2
5个回答
29
投票

在Gazxswpoi中将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> 将让JSF将forward.jsf?action=outcome1&actionParam=123action参数设置为actionParamactionactionParam属性。

创建一个小视图ForwardBean(这么小,它适合默认的响应缓冲区(通常为2KB),以便它可以由导航处理程序重置,否则你需要增加servletcontainer配置中的响应缓冲区),它会调用bean方法forward.xhtmlbeforePhase

f:view

<!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

并更新要在<!DOCTYPE html> <html>#{forward}</html> 上调用的navigate()方法(将在bean构造和所有托管属性设置后调用):

@PostConstruct

它具有相同的效果,但视图方面并非真正自我记录。所有它基本上都是打印@PostConstruct public void navigate() { // ... } (并因此隐含地构建bean,如果还不存在)。


注意JSF2用户,有一种更简洁的方法,用ForwardBean#toString()传递参数,以及更强大的方法来处理<f:viewParam>的重定向/导航。另见:



2
投票

你应该使用action而不是actionListener:

FacesContext context = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse)context.getExternalContext().getResponse();
response.sendRedirect("somePage.jsp");

并以密切的方式你正确的事情:

<h:commandLink id="close" action="#{bean.close}" value="Close" immediate="true" 
                                   />

index是你的一个页面(index.xhtml)

当然,所有这些工作人员都应该写在我们的原始页面中,而不是中间页面。在public String close() { return "index?faces-redirect=true"; } 方法中,您可以使用参数动态选择重定向的位置。


1
投票

编辑2

我终于通过实现我的前进动作找到了一个解决方案:

close()

它起作用(至少关于我的第一次测试),但我仍然不喜欢它的实现方式...更好的主意?


编辑此解决方案不起作用。实际上,当调用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(); } } } } 函数时,JSF生命周期已经启动,然后无法重新创建新请求。


解决这个问题的一个想法,但我真的不喜欢它,是在doForward()方法之一强制doForward()动作:

setBindedInputHidden()

此解决方案不涉及任何Javascript调用,并且 作品 不起作用。


0
投票

假设foo.jsp是你的jsp文件。以下代码是您想要重定向的按钮。

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);
}

现在我们将检查在java(服务)类中引导的方法

<h:commandButton value="Redirect" action="#{trial.enter }"/>  

现在这是faces-config.xml文件的一部分

 public String enter() {
            if (userName.equals("xyz") && password.equals("123")) {
                return "enter";
            } else {
                return null;
            }
        } 
© www.soinside.com 2019 - 2024. All rights reserved.