2010-10-27 2 views
26

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

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

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

Так, например, я могу иметь такого рода URLs:

  • 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-бин. Они будут использоваться для хранения значения параметра 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

+0

Я не уверен, является ли это лучше делать или нет, но вы даете пользователю ссылку на кнопку и с помощью промежуточного XHTML и делает JavaScript клик, но почему вы не указываете его на сервлет, и оттуда вы можете справиться с этим сценарием, я имею в виду вместо промежуточного xhtml, почему бы не использовать сервлет. Я не думаю, что его плохо добавить наш собственный сервлет в jsf env –

+0

@org: Это, безусловно, можно сделать без сервлетов. Использование сервлета только добавит дополнительную головную боль обслуживания, потому что у нее нет мгновенного доступа к случаям навигации. – BalusC

+0

@BalusC Спасибо, я хочу знать, как? , Можно ли напрямую использовать скрипты jsp на xhtml? –

ответ

28

Установить параметры запроса 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), который вызывает метод боб на 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 (который будет вызываться после создания фасоли и всех настроек управляемого объекта):

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

Он имеет тот же эффект, однако сторона обзора на самом деле не является самодокументирующей. Все это в основном заключается в печати ForwardBean#toString() (и при этом неявно конструирует bean-компонент, если он еще не представлен).


Примечания для пользователей JSF2, есть уборщик способ передачи параметров с <f:viewParam> и более надежным способом обработки перенаправлять/навигацию по <f:event type="preRenderView">. Смотрите также среди других:

+0

Мне нравится ваша идея использования ' '. Однако мои первые тесты не являются успешными, поскольку метод * beforePhase * не вызывается. Я не понимаю, почему ... – romaintaz

+0

Я до сих пор не понимаю, почему первое решение не работает (возможно, я создам для этого отдельный пост), но второе решение действительно работает! Спасибо большое! – romaintaz

+0

Был ли представлен аргумент «PhaseEvent»? Я должен признать, что я тестировал 2,1-ночную версию с face-config, объявленной как 1.2 (так что функции 2.x не будут работать). Этот атрибут был, кстати, введен в 1.2 (так что выиграл ' t работает на 1.1 или старше). Но это также '@ PostConstruct'. – BalusC

1

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

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

и закрытым способом вы правы что-то вроде:

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

, где индекс является одной из ваших страниц (index.xhtml)

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

+0

Использование 'action' или' actionListener' абсолютно не влияет на мою текущую проблему. – romaintaz

+0

И OP использует JSF 1.x, а не 2.x. Кроме того, перенаправление приведет к потере параметров запроса. – BalusC

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

Не отвечает на мой вопрос. Где вы хотите, чтобы я поставил этот код? И как это можно назвать без какого-либо взаимодействия с пользователем или с вызовом Javascript? – romaintaz

1

Edit 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 call, и работы нет работа.

+1

Это действительно неудобно :). 'HandleNavigation()' здесь совершенно не нужен (он, в свою очередь, переопределяется 'response.sendRedirect()'), а 'ExternalContext' имеет метод' redirect() '. – BalusC

Смежные вопросы