Подтвердить что ты не робот

Как сделать перенаправление в JSF

У меня есть веб-приложение, в котором пользователи могут быть отправлены непосредственно на определенные страницы (например, страницу, где он может просматривать или редактировать элемент). Для этого мы предоставляем определенный URL-адрес. Эти URL-адреса расположены вне текущего веб-приложения (т.е. Могут присутствовать в другом веб-приложении или в электронном письме).

URL-адрес выглядит как http://myserver/my-app/forward.jsf?action=XXX&param=YYY, где:

  • действие представляет страницу, на которой пользователь будет перенаправлен. Вы можете рассматривать это как from-outcome любого действия JSF в navigation-case в faces-config.xml.
  • actionParam - это параметр для предыдущего действия (обычно идентификатор элемента)

Так, например, у меня могут быть такие URL-адреса:

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

Как вы можете видеть, я связываю два компонента <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 не поддерживается).

Итак, мой вопрос: как реорганизовать эту функцию перенаправления/пересылки?

Техническая информация

Java 1.6, JSF 1.2, Facelets, Richfaces

4b9b3361

Ответ 1

Задайте параметры запроса 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 (настолько маленький, что он подходит в буфере ответа по умолчанию (часто 2 КБ), чтобы его можно было сбросить с помощью манипулятора навигации, в противном случае вы должны увеличить буфер ответа в конфигурации servletcontainer), который вызывает метод bean на beforePhase 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">. См. Также среди других:

Ответ 2

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

Ответ 3

вы должны использовать действие вместо actionListener:

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

и в близком методе вы правы:

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

где index - одна из ваших страниц (index.xhtml)

Конечно, весь этот персонал должен быть написан на нашей исходной странице, а не в промежуточном. И внутри метода close() вы можете использовать параметры для динамического выбора перенаправления.

Ответ 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 уже запущен, а затем воссоздать новый запрос невозможно.


Одна из идей для решения этой проблемы, но мне это не очень нравится, заключается в том, чтобы принудительно выполнить действие 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, а works работает не.

Ответ 5

Предположим, что 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;
            }
        } 

и теперь это часть файлаface-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>