代码之家  ›  专栏  ›  技术社区  ›  Romain Linsolas

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

  •  29
  • Romain Linsolas  · 技术社区  · 15 年前

    外部 当前的web应用程序(即它们可以出现在另一个web应用程序或电子邮件中)。

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

    • 行动 表示将重定向用户的页。你可以认为这是 from-outcome navigation-case 在里面 faces-config.xml
    • 动作参数 是前一个操作的参数(通常是项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 ).


    当前实施

    <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中的组件。它们将被用来存储 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)。

    如何重构此重定向/转发功能?

    技术资料

    5 回复  |  直到 7 年前
        1
  •  30
  •   Community Mohan Dere    8 年前

    在中将GET query参数设置为托管属性 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 参数为 行动 动作参数 的属性 ForwardBean

    创建小视图 forward.xhtml (小到可以放入默认响应缓冲区(通常是2KB),这样导航处理程序可以重置它,否则您必须在servletcontainer的配置中增加响应缓冲区),它调用 beforePhase f:view :

    <!DOCTYPE html>
    <html xmlns:f="http://java.sun.com/jsf/core">
        <f:view beforePhase="#{forward.navigate}" />
    </html>
    

    这个 可能是这样的:

    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>
    

    前进.xhtml

    <!DOCTYPE html>
    <html>#{forward}</html>
    

    并更新 navigate() 要在其上调用的方法 @PostConstruct

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

    它具有相同的效果,但是视图端并不是真正的自我文档化。它的基本功能就是打印 ForwardBean#toString() (如果还不存在,则在此隐式构造bean)。


    <f:viewParam> 以及更健壮的处理重定向/导航的方法 <f:event type="preRenderView"> . 另见:

        2
  •  5
  •   Knatpolsar    15 年前
    FacesContext context = FacesContext.getCurrentInstance();
    HttpServletResponse response = (HttpServletResponse)context.getExternalContext().getResponse();
    response.sendRedirect("somePage.jsp");
    
        3
  •  2
  •   Brad Larson    11 年前

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

    在近距离方法中,你可以纠正如下问题:

    public String close() {
       return "index?faces-redirect=true";
    }
    

    当然,所有这些员工都应该写在我们的原始页面上,而不是中间页面。 close() 方法可以使用参数动态选择重定向的位置。

        4
  •  1
  •   Romain Linsolas    15 年前

    编辑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() 方法:

    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调用,并且

        5
  •  0
  •   berkancetin    7 年前

    假设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;
                }
            } 
    

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