Pregunta

Tengo una aplicación web en la que los usuarios pueden enviar directamente a algunas páginas específicas (como una página en la que puede ver o editar un elemento). Para lograr eso, proporcionamos una dirección URL específica. Estas direcciones URL se encuentran fuera la corriente de aplicación web (es decir, que pueden estar presentes en otra aplicación web, o en un correo electrónico).

Las miradas URL como http://myserver/my-app/forward.jsf?action=XXX&param=YYY, donde:

  • acción representa la página en la que se redirige al usuario. Se puede considerar esto como el 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 direcciones 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 (frijol) que verificará algunas restricciones de seguridad (es decir, es el usuario permitido ver / editar el elemento correspondiente?) Y luego redirigir al usuario a la página correcta (como edit.xhtml, view.xhtml o access-denied.xhtml).


aplicación de corriente

En la actualidad, tenemos una forma básica para lograr el delantero. 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. Serán utilizados para almacenar el valor del parámetro de la petición tanto action y actionParam (usando FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("actiontParam");). También proporciono el método que doForward que será llamado inmediatamente cuando se vuelve a la página, que redirigirá (de nuevo) 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:

  • No me gusta la forma en que se implementa. Estoy seguro de que puedo tener algo más simple (utilizando un servlet?).
  • Esta solución es utilizar Javascript, y no debe usar Javascript (ya que este avance puede ser utilizado por los usuarios de Blackberry de edad, en los que no se admite el Javascript).

Así que mi pregunta es cómo refactorizar esta redirección / función hacia delante?

Información técnica

Java 1.6, JSF 1.2, Facelets, RichFaces

¿Fue útil?

Solución

Configurar 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 propiedades action y actionParam del ForwardBean.

Crea 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 una método de bean 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 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 las entradas <redirect /> 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 el uso forward.xhtml como

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

y actualizar el método navigate() que se invoca en @PostConstruct (que será invocado después de la construcción de frijol y todas las configuraciones de propiedad administrada):

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

Tiene el mismo efecto, sin embargo, la vista lateral no es realmente auto-documentado. Todo lo que hace básicamente es la impresión ForwardBean#toString() (y de esta manera implícita la construcción del frijol si no está presente todavía).


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

Otros consejos

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

se debe utilizar en lugar de la acción 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 esto el personal debe estar escrito 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.

Editar 2

Finalmente encontré una solución mediante la implementación de mi acción hacia adelante como lo siguiente:

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 ... Cualquier idea mejor?


Editar Esta solución hace no trabajo. De hecho, cuando la función doForward() se llama, ya se ha iniciado el ciclo de vida de JSF, y volver a crear una nueva petición no es posible.


Una idea para resolver este problema, pero realmente no me gusta, es forzar la acción doForward() durante uno de los métodos 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 funciona tiene no trabajo.

Supongamos que foo.jsp es el archivo JSP. y siguiente código es el botón que desea hacer redirección.

<h:commandButton value="Redirect" action="#{trial.enter }"/>  

Y ahora vamos a comprobar el método para dirigir en su Java (servicio) de clase

 public String enter() {
            if (userName.equals("xyz") && password.equals("123")) {
                return "enter";
            } else {
                return null;
            }
        } 

y ahora se trata de una parte del archivo faces-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>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top