web-dev-qa-db-fra.com

Comment faire une redirection au chargement de la page dans JSF 1.x

J'ai une application Web où les utilisateurs peuvent être envoyés directement vers certaines pages spécifiques (comme une page où il peut afficher ou modifier un élément). Pour ce faire, nous fournissons une URL spécifique. Ces URL se trouvent à l'extérieur l'application Web actuelle (c'est-à-dire qu'elles peuvent être présentes dans une autre application Web ou dans un e-mail).

L'URL ressemble à http://myserver/my-app/forward.jsf?action=XXX&param=YYY, Où:

  • action représente la page où l'utilisateur sera redirigé. Vous pouvez considérer cela comme le from-outcome De toute action JSF dans navigation-case Dans faces-config.xml.
  • actionParam est un paramètre pour l'action précédente (généralement un ID d'élément)

Ainsi, par exemple, je peux avoir ce type d'URL:

  • http://myserver/my-app/forward.jsf?action=viewItem&actionParam=1234
  • http://myserver/my-app/forward.jsf?action=editItem&actionParam=1234

Bien sûr, j'ai une classe Java (bean) qui vérifiera certaines contraintes de sécurité (c'est-à-dire que l'utilisateur est autorisé à voir/modifier l'élément correspondant?), Puis à rediriger l'utilisateur vers la bonne page (comme edit.xhtml, view.xhtml ou access-denied.xhtml).


Implémentation actuelle

Actuellement, nous avons un moyen de base pour faire avancer les choses. Lorsque l'utilisateur clique sur le lien, la page XHTML suivante est appelée:

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

Comme vous pouvez le voir, je lie deux composants <h:inputHidden> Dans mon bean Java. Ils seront utilisés pour stocker la valeur des deux action et actionParam paramètre de requête (en utilisant FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("actiontParam");). Je fournis également la méthode doForward celle qui sera appelée immédiatement lors du rendu de la page, qui redirigera (à nouveau) l'utilisateur vers la page réelle La méthode est la suivante:

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);
}

Cette solution fonctionne, mais j'ai deux problèmes avec cela:

  • Je n'aime pas la façon dont il est mis en œuvre. Je suis sûr que je peux avoir quelque chose de plus simple (en utilisant un Servlet?).
  • Cette solution utilise Javascript, et je ne dois pas utiliser Javascript (car ce transfert peut être utilisé par les anciens utilisateurs de Blackberry, où Javascript n'est pas pris en charge).

Donc ma question est comment refactoriser cette fonctionnalité de redirection/transfert?

Informations techniques

Java 1.6, JSF 1.2, Facelets, Richfaces

30
Romain Linsolas

Définissez les paramètres de requête GET en tant que propriétés gérées dans faces-config.xml Afin de ne pas avoir à les rassembler manuellement:

<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 cette façon, la requête forward.jsf?action=outcome1&actionParam=123 Permettra à JSF de définir les paramètres action et actionParam en tant que propriétés action et actionParam des ForwardBean.

Créez une petite vue forward.xhtml (Si petite qu'elle tient dans le tampon de réponse par défaut (souvent 2 Ko) afin qu'elle puisse être réinitialisée par le gestionnaire de navigation, sinon vous devez augmenter le tampon de réponse dans la configuration du conteneur de servlet), qui invoque une méthode bean sur beforePhase du f:view:

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

Le ForwardBean peut ressembler à ceci:

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

Le navigation-rule Parle de lui-même (notez les entrées <redirect /> Qui feraient ExternalContext#redirect() au lieu de ExternalContext#dispatch() sous les couvertures):

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

Une alternative consiste à utiliser forward.xhtml Comme

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

et mettez à jour la méthode navigate() à invoquer sur @PostConstruct (qui sera invoquée après la construction du bean et tous les paramètres de propriété gérée):

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

Cela a le même effet, mais la vue n'est pas vraiment auto-documentée. Tout ce qu'il fait est d'imprimer ForwardBean#toString() (et par la présente de construire implicitement le bean s'il n'est pas encore présent).


Remarque pour les utilisateurs JSF2, il existe un moyen plus propre de passer des paramètres avec <f:viewParam> Et un moyen plus robuste de gérer la redirection/navigation par <f:event type="preRenderView">. Voir aussi entre autres:

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

vous devez utiliser action au lieu de actionListener:

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

et en étroite méthode vous redressez quelque chose comme:

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

où index est l'une de vos pages (index.xhtml)

Bien sûr, tout ce personnel doit être écrit dans notre page d'origine, pas dans l'intermédiaire. Et à l'intérieur de la méthode close(), vous pouvez utiliser les paramètres pour choisir dynamiquement où rediriger.

2
Vladimir Ivanov

Édition 2

J'ai finalement trouvé une solution en mettant en œuvre mon action avancée comme ça:

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();
            }
        }
    }
}

Cela fonctionne (du moins en ce qui concerne mes premiers tests), mais je n'aime toujours pas la façon dont il est mis en œuvre ... Une meilleure idée?


Modifier Cette solution ne fonctionne pas . En effet, lorsque la fonction doForward() est appelée, le cycle de vie JSF a déjà été démarré, puis recréer une nouvelle requête n'est pas possible.


Une idée pour résoudre ce problème, mais je ne l'aime pas vraiment, est de forcer l'action doForward() pendant l'une des méthodes 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);
}

Cette solution n'implique aucun appel Javascript, et travaux  ne fonctionne pas .

1
Romain Linsolas

Supposons que foo.jsp soit votre fichier jsp. et le code suivant est le bouton que vous souhaitez rediriger.

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

Et maintenant, nous allons vérifier la méthode de direction dans votre classe Java (service)

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

et maintenant cela fait partie du fichier 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>
0
berkancetin