30

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

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

  • action表示用户将被重定向到的页面。您可以将其视为in 中from-outcome的任何 JSF 操作navigation-casefaces-config.xml
  • actionParam是上一个动作的参数(一般是item 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>

如您所见,我<h:inputHidden>在我的 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(因为此转发可能由不支持 Javascript 的老黑莓用​​户使用)。

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

技术信息

Java 1.6、JSF 1.2、Facelets、Richfaces

4

5 回答 5

30

将 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 将actionactionParam参数设置为.actionactionParamForwardBean

创建一个小视图forward.xhtml(小到适合默认响应缓冲区(通常为 2KB),以便可以由导航处理程序重置,否则您必须在 servletcontainer 的配置中增加响应缓冲区),它调用 beanbeforePhase方法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()要调用的方法@PostConstruct(将在 bean 的构造和所有托管属性设置后调用):

@PostConstruct
public void navigate() {
    // ...
}    

它具有相同的效果,但是视图端并不是真正的自我记录。它基本上所做的只是打印ForwardBean#toString()(如果还没有,则隐式构造 bean)。


对于 JSF2 用户,请注意,<f:viewParam>通过<f:event type="preRenderView">. 另见:

于 2010-11-04T17:28:04.507 回答
5
FacesContext context = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse)context.getExternalContext().getResponse();
response.sendRedirect("somePage.jsp");
于 2010-10-27T12:30:59.370 回答
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()方法内部,您可以使用参数动态选择重定向的位置。

于 2010-10-27T12:17:22.667 回答
1

编辑 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 生命周期已经开始了,再重新创建一个新的请求是不可能的。


解决这个问题的一个想法,但我不太喜欢它,是doForward()在其中一种方法期间强制执行操作setBindedInputHidden()

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 调用,且工作正常

于 2010-11-03T12:55:34.700 回答
0

假设 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>
于 2019-04-17T07:11:37.693 回答