java - vista - Cómo hacer una redirección en JSF
no se puede encontrar el caso de navegación coincidente del id de vista (4)
Establezca los parámetros de consulta GET como propiedades administradas en faces-config.xml
para que no tenga que recopilarlos manualmente:
<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>
De esta forma, la solicitud forward.jsf?action=outcome1&actionParam=123
permitirá a JSF establecer los parámetros action
y actionParam
como action
y actionParam
propiedades del ForwardBean
.
Cree una vista pequeña forward.xhtml
(tan pequeña que cabe en el búfer de respuesta predeterminado (a menudo 2KB) para que el navegador pueda restablecerla; de lo contrario, debe aumentar el búfer de respuesta en la configuración de servletcontainer), que invoca un bean método en beforePhase
de la f:view
:
<!DOCTYPE html>
<html xmlns:f="http://java.sun.com/jsf/core">
<f:view beforePhase="#{forward.navigate}" />
</html>
El ForwardBean
puede verse así:
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.
}
La navigation-rule
habla por sí sola (observe las entradas <redirect />
que harían ExternalContext#redirect()
lugar de ExternalContext#dispatch()
debajo de las cubiertas):
<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>
Una alternativa es usar forward.xhtml
como
<!DOCTYPE html>
<html>#{forward}</html>
y actualice el método navigate()
que se invocará en @PostConstruct
(que se invocará después de la construcción de bean y toda la configuración de propiedad administrada):
@PostConstruct
public void navigate() {
// ...
}
Tiene el mismo efecto, sin embargo, el lado de la vista no es realmente autodocumentado. Todo lo que hace básicamente es imprimir ForwardBean#toString()
(y de este modo construir implícitamente el bean si aún no está presente).
Nota para los usuarios de JSF2, hay una forma más clara de pasar parámetros con <f:viewParam>
y una forma más sólida de manejar la redirección / navegación mediante <f:event type="preRenderView">
. Ver también entre otros:
Tengo una aplicación web donde los usuarios pueden enviarse directamente a algunas páginas específicas (como una página donde puede ver o editar un elemento). Para lograr eso, proporcionamos una url específica. Estas URL se encuentran fuera de la aplicación web actual (es decir, pueden estar presentes en otra aplicación web o en un correo electrónico).
La URL se ve como http://myserver/my-app/forward.jsf?action=XXX¶m=YYY
, donde:
- action representa la página donde se redirigirá al usuario. Puede considerar esto como el
from-outcome
de cualquier acción JSF ennavigation-case
enfaces-config.xml
. - actionParam es un parámetro para la acción anterior (generalmente una ID de artículo)
Entonces, por ejemplo, puedo tener este tipo de URL:
-
http://myserver/my-app/forward.jsf?action=viewItem&actionParam=1234
-
http://myserver/my-app/forward.jsf?action=editItem&actionParam=1234
Por supuesto, tengo una clase Java (bean) que verificará algunas restricciones de seguridad (es decir, ¿se le permite al usuario ver / editar el elemento correspondiente?) Y luego redirigir al usuario a la página correcta (como edit.xhtml
, view.xhtml
o access-denied.xhtml
).
Implementación actual
Actualmente, tenemos una forma básica de lograr el avance. Cuando el usuario hace clic en el enlace, se llama a la siguiente página 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>
Como puede ver, enlace dos componentes <h:inputHidden>
en mi bean Java. Se usarán para almacenar el valor de action
y parámetro de solicitud actionParam
(usando FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("actiontParam");
). También proporciono el método doForward
que se llamará inmediatamente cuando se represente la página, lo que redirigirá (nuevamente) al usuario a la página real. El método es:
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);
}
Esta solución está funcionando, pero tengo dos problemas con eso:
- No me gusta la forma en que se implementa. Estoy seguro de que puedo tener algo más sencillo (¿usar un servlet?).
- Esta solución usa Javascript y no debo usar Javascript (ya que este reenvío puede ser utilizado por usuarios antiguos de Blackberry, donde el Javascript no es compatible).
¿Entonces mi pregunta es cómo refactorizar esta función de redirección / reenvío?
Información técnica
Java 1.6, JSF 1.2, Facelets, Richfaces
deberías usar action en lugar de actionListener:
<h:commandLink id="close" action="#{bean.close}" value="Close" immediate="true"
/>
y en método cercano tienes derecho a algo como:
public String close() {
return "index?faces-redirect=true";
}
donde index es una de sus páginas (index.xhtml)
Por supuesto, todo este personal debe escribirse en nuestra página original, no en la intermedia. Y dentro del método close()
puede usar los parámetros para elegir dinámicamente a dónde redirigir.
Editar 2
Finalmente encontré una solución al implementar mi acción hacia adelante así:
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();
}
}
}
}
Funciona (al menos con respecto a mis primeras pruebas), pero todavía no me gusta la forma en que se implementa ... ¿Alguna idea mejor?
Editar esta solución no funciona. De hecho, cuando se llama a la función doForward()
, el ciclo de vida JSF ya se ha iniciado y luego se vuelve a crear una nueva solicitud.
Una idea para resolver este problema, pero realmente no me gusta, es forzar la acción doForward()
durante uno de los 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);
}
Esta solución no implica ninguna llamada de Javascript, y las obras no funcionan.
FacesContext context = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse)context.getExternalContext().getResponse();
response.sendRedirect("somePage.jsp");