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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4032825/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 10:45:22  来源:igfitidea点击:

How to make a redirection on page load in JSF 1.x

javajsfjsf-1.2

提问by Romain Linsolas

I have a web-application where the users can be sent directly to some specific pages (such as a page where he can view or edit an item). To achieve that, we provide a specific url. These urls are located outsidethe current web-application (i.e. they can be present in another web-application, or in an email).

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

The url looks like http://myserver/my-app/forward.jsf?action=XXX&param=YYY, where:

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

  • actionrepresents the page where the user will be redirected. You can consider this as the from-outcomeof any JSF action in navigation-casein faces-config.xml.
  • actionParamis a parameter for the previous action (generally an item ID)
  • action表示用户将被重定向到的页面。你可以认为这是在from-outcome任何JSF的行动navigation-casefaces-config.xml
  • actionParam是上一个动作的参数(一般是item ID)

So for example, I can have these kind of urls:

例如,我可以拥有以下网址:

  • http://myserver/my-app/forward.jsf?action=viewItem&actionParam=1234
  • http://myserver/my-app/forward.jsf?action=editItem&actionParam=1234
  • http://myserver/my-app/forward.jsf?action=viewItem&actionParam=1234
  • http://myserver/my-app/forward.jsf?action=editItem&actionParam=1234

Of course, I have a Java class (bean) that will check some security constraints (i.e. is the user allowed to see / edit the corresponding item?) and then redirect the user to the correct page (such as edit.xhtml, view.xhtmlor access-denied.xhtml).

当然,我有一个 Java 类(bean),它会检查一些安全约束(即是否允许用户查看/编辑相应的项目?),然后将用户重定向到正确的页面(例如edit.xhtmlview.xhtmlaccess-denied.xhtml)。



Current implementation

当前实施

Currently, we have a basic way to accomplish the forward. When the user clicks on the link, the following XHTML page is called:

目前,我们有一个基本的方法来完成前进。当用户单击链接时,将调用以下 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>

As you can see, I bind two <h:inputHidden>components in my Java bean. They will be used to store the value of both actionand actionParamrequest parameter (using FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("actiontParam");). I also provide the doForwardmethod that which will be called immediately when the page is rendered, which will redirect (again) the user to the real page. The method is:

如您所见,我<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);
}


This solution is working, but I have two problems with that:

此解决方案有效,但我有两个问题:

  • I don't like the way it is implemented. I'm sure that I can have something simplier (using a Servlet?).
  • This solution is using Javascript, and I must not use Javascript (as this forward may be used by old Blackberry users, where the Javascript is not supported).
  • 我不喜欢它的实施方式。我确信我可以有更简单的东西(使用 Servlet?)。
  • 此解决方案使用 Javascript,我不能使用 Javascript(因为此转发可能被不支持 Javascript 的老黑莓用​​户使用)。

So my question is how to refactor this redirection / forward feature?

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

Technical information

技术信息

Java 1.6, JSF 1.2, Facelets, Richfaces

Java 1.6、JSF 1.2、Facelets、Richfaces

采纳答案by BalusC

Set the GET query parameters as managed properties in faces-config.xmlso that you don't need to gather them manually:

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

This way the request forward.jsf?action=outcome1&actionParam=123will let JSF set the actionand actionParamparameters as actionand actionParamproperties of the ForwardBean.

这样的要求forward.jsf?action=outcome1&actionParam=123会让JSF设置actionactionParam参数actionactionParam的性质ForwardBean

Create a small view forward.xhtml(so small that it fits in default response buffer (often 2KB) so that it can be resetted by the navigationhandler, otherwise you've to increase the response buffer in the servletcontainer's configuration), which invokes a bean method on beforePhaseof the f:view:

创建一个小的视图forward.xhtml(如此之小,它在默认响应缓冲(通常2KB适合),这样它就可以通过navigationhandler被重置了,否则,你已经增加了servletcontainer的配置响应缓冲区),它调用bean方法上beforePhase的的f:view

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

The ForwardBeancan look like this:

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

The navigation-rulespeaks for itself (note the <redirect />entries which would do ExternalContext#redirect()instead of ExternalContext#dispatch()under the covers):

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>


An alternative is to use forward.xhtmlas

另一种方法是使用forward.xhtml作为

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

and update the navigate()method to be invoked on @PostConstruct(which will be invoked after bean's construction and all managed property setting):

并更新navigate()要调用的方法@PostConstruct(将在 bean 的构造和所有托管属性设置后调用):

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

It has the same effect, however the view side is not really self-documenting. All it basically does is printing ForwardBean#toString()(and hereby implicitly constructing the bean if not present yet).

它具有相同的效果,但是视图方面并不是真正的自我记录。它基本上所做的就是打印ForwardBean#toString()(如果尚未存在,则在此隐式构造 bean)。



Note for the JSF2 users, there is a cleaner way of passing parameters with <f:viewParam>and more robust way of handling the redirect/navigation by <f:event type="preRenderView">. See also among others:

请注意,对于 JSF2 用户,有一种更简洁的方式来传递参数,<f:viewParam>并且可以通过更强大的方式处理重定向/导航<f:event type="preRenderView">。另见:

回答by Vladimir Ivanov

you should use action instead of actionListener:

你应该使用 action 而不是 actionListener:

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

and in close method you right something like:

在 close 方法中,您可以正确操作,例如:

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

where index is one of your pages(index.xhtml)

其中 index 是您的页面之一(index.xhtml)

Of course, all this staff should be written in our original page, not in the intermediate. And inside the close()method you can use the parameters to dynamically choose where to redirect.

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

回答by jnr

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

回答by Romain Linsolas

Edit 2

编辑 2

I finally found a solution by implementing my forward action like that:

我终于通过实施我的前向行动找到了一个解决方案:

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

It works (at least regarding my first tests), but I still don't like the way it is implemented... Any better idea?

它有效(至少对于我的第一次测试),但我仍然不喜欢它的实现方式......有更好的主意吗?



EditThis solution does notwork. Indeed, when the doForward()function is called, the JSF lifecycle has already been started, and then recreate a new request is not possible.

编辑该解决方案并不能正常工作。确实,当doForward()函数被调用时,JSF生命周期已经开始,再重新创建一个新的请求是不可能的。



One idea to solve this issue, but I don't really like it, is to force the doForward()action during one of the setBindedInputHidden()method:

解决这个问题的一个想法,但我不太喜欢它,是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);
}

This solution does not involve any Javascript call, and worksdoes notwork.

该解决方案不涉及任何JavaScript调用,而作品不能正常工作。

回答by berkancetin

Assume that foo.jsp is your jsp file. and following code is the button that you want do redirect.

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

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

And now we'll check the method for directing in your java (service) class

现在我们将检查您的 java(服务)类中的定向方法

 public String enter() {
            if (userName.equals("xyz") && password.equals("123")) {
                return "enter";
            } else {
                return null;
            }
        } 

and now this is a part of faces-config.xml file

现在这是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>