2010-10-27 11 views
26

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 están ubicadas en fuera de la aplicación web actual (es decir, pueden estar presentes en otra aplicación web o en un correo electrónico).Cómo hacer una redirección en JSF

La url parece http://myserver/my-app/forward.jsf?action=XXX&param=YYY, donde:

  • acción representa la página en la que se redirige al usuario. Puede considerar esto como from-outcome de cualquier acción JSF en navigation-case en faces-config.xml.
  • actionParam es un parámetro para la acción anterior (generalmente un ID de elemento)

Así, por ejemplo, que puede tener este tipo de URLs:

  • 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 de Java (bean) que comprobará algunos restricciones de seguridad (es decir ¿el usuario puede ver/editar el artículo correspondiente?) y luego redirigir al usuario a la página correcta (como edit.xhtml, view.xhtml o access-denied.xhtml).


aplicación actual

En la actualidad, tenemos una forma básica para lograr el avance. Cuando el usuario hace clic en el enlace, la siguiente página XHTML se llama:

<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 se puede ver, se unen dos componentes <h:inputHidden> en mi bean Java. Se usarán para almacenar el valor del parámetro de solicitud action y 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 funciona, pero tengo dos problemas con eso:

  • que 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 no es compatible con Javascript).

Así que mi pregunta es cómo refactorizar esta característica de redirección/avance?

Información técnica

Java 1.6, JSF 1.2, Facelets, richfaces

+0

No estoy seguro de si es mejor hacer o no, pero le está dando a los usuarios un enlace para hacer clic y utilizando XHTML intermedio y haciendo Javascript haga clic en, pero ¿Por qué no lo está apuntando a un servlet y desde allí puede manejar este escenario que quiero decir en lugar de xhtml intermedio por qué no utilizar servlet. No creo que sea malo agregar nuestro propio servlet en jsf env –

+0

@org: esto sin duda se puede hacer sin servlets. Usar un servlet solo agregaría dolores de cabeza adicionales debido a que no tiene acceso instantáneo a los casos de navegación. – BalusC

+0

@BalusC Gracias, quiero saber, ¿cómo? , ¿Podemos usar scriptlets jsp en xhtml directamente? –

Respuesta

28

establecer los parámetros de consulta GET como propiedades administradas en faces-config.xml de manera que no es necesario para reunir de forma manual:

<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 manera la solicitud forward.jsf?action=outcome1&actionParam=123 le permitirá establecer los parámetros JSF action y actionParam como action y actionParam propiedades del ForwardBean.

Crear una pequeña vista forward.xhtml (tan pequeño que cabe en la memoria de respuesta por defecto (a menudo de 2 KB), de modo que pueda ser reseteado por el NavigationHandler, de lo contrario usted tiene que aumentar la memoria de respuesta en la configuración de la servletcontainer), que invoca un método de frijol en beforePhase del f:view:

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

el ForwardBean puede tener este aspecto:

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

el navigation-rule habla por sí mismo (tenga en cuenta los <redirect /> entradas que hacer ExternalContext#redirect() en lugar de ExternalContext#dispatch() bajo las sábanas):

<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 utilizar forward.xhtml como

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

y actualizar el método navigate() para invocarse 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 implícitamente construir el bean si no está presente aún).


Nota para los usuarios JSF2, hay una manera más limpia de pasar parámetros con <f:viewParam> y más robusto manera de manejar la redirección/navegación por <f:event type="preRenderView">. Véase también, entre otros:

+0

Me gusta su idea de usar ' '. Sin embargo, mis primeras pruebas no son exitosas, ya que no se llama al método * beforePhase *. No entiendo por qué ... – romaintaz

+0

Todavía no entiendo por qué la primera solución no funciona (tal vez crearé una publicación separada para eso), pero la segunda solución está funcionando! Muchas gracias! – romaintaz

+0

¿Estaba presente el argumento 'PhaseEvent'? ¿Qué JSF impl/versión exactamente? Debo admitir que probé en 2.1-nightly con faces-config declarado como 1.2 (para que las características 2.x no funcionen). Ese atributo fue por cierto introducido en 1.2 (así ganó t funciona en absoluto en 1.1 o más). Pero también lo es '@ PostConstruct'. – BalusC

1

se debe utilizar la acción en lugar de actionListener:

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

y en el método de cerca que algo bien como:

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

donde el índice es una de sus páginas (index.xhtml)

Por supuesto, todo este personal debe escribirse en nuestra página original, no en el intermedio. Y dentro del método close() puede utilizar los parámetros para elegir dinámicamente dónde redirigir.

+0

El uso de 'action' o' actionListener' no tiene ningún impacto en mi problema actual. – romaintaz

+0

Y el OP usa JSF 1.x, no 2.x. Además, la redirección hará que los parámetros de solicitud se pierdan. – BalusC

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

No responde mi pregunta. ¿Dónde quieres que ponga este código? ¿Y cómo se puede invocar sin interacción del usuario ni llamada de Javascript? – romaintaz

1

Editar 2

finalmente he encontrado una solución mediante la implementación de mi acción hacia adelante de esa manera:

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 Don me gusta la forma en que se implementa ... ¿Alguna idea mejor?


Editar Esta solución hace no trabajo. 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() método:

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 ningún Javascript llaman, y obras hace no trabajo.

+1

Esto es realmente incómodo :) El 'handleNavigation()' es completamente innecesario aquí (a su vez ha sido anulado por 'response.sendRedirect()') y 'ExternalContext' tiene un método' redirect() '. – BalusC

Cuestiones relacionadas