Как сделать перенаправление в JSF
У меня есть веб-приложение, в котором пользователи могут быть отправлены непосредственно на определенные страницы (например, страницу, где он может просматривать или редактировать элемент). Для этого мы предоставляем определенный URL-адрес. Эти URL-адреса расположены вне текущего веб-приложения (т.е. Могут присутствовать в другом веб-приложении или в электронном письме).
URL-адрес выглядит как http://myserver/my-app/forward.jsf?action=XXX¶m=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
Ответы
Ответ 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>