Comment faire une redirection dans JSF

j'ai une application web où les utilisateurs peuvent être envoyés directement à certaines pages (comme une page où il peut visualiser ou de modifier un élément). Pour ce faire, nous fournissons une url spécifique. Ces urls sont situées en dehors de l'application Web actuelle (i.e. ils peuvent être présents dans une autre application web, ou dans un email).

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 de l'action précédente (généralement un élément ID)

Ainsi, par exemple, je peux avoir ce genre 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 va vérifier certaines contraintes de sécurité (i.e. est-ce que l'utilisateur est autorisé à voir / modifier l'élément correspondant?) et ensuite rediriger l'utilisateur vers la bonne page (comme edit.xhtml , view.xhtml ou access-denied.xhtml ).


application actuelle

Actuellement, nous avons un moyen de base pour accomplir l'avant. Lorsque l'utilisateur clique sur le lien, la page XHTML suivante s'appelle:

<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 <h:inputHidden> composants dans mon haricot Java. Ils seront utilisés pour stocker la valeur du paramètre de requête action et actionParam (en utilisant FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("actiontParam"); ). Je fournis également la méthode doForward qui sera appelée immédiatement lorsque la page est rendue, ce qui redirigera (à nouveau) l'utilisateur vers la page réelle. La méthode est:

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 forward peut être utilisé par les anciens utilisateurs de Blackberry, où Javascript n'est pas supporté).

donc ma question Est comment reformuler cette fonction de redirection / forward?

informations techniques

Java 1.6, JSF 1.2, Facelets, Richfaces

27
demandé sur Xtreme Biker 2010-10-27 15:45:54

4 réponses

définissez les paramètres de requête GET en tant que propriétés gérées dans faces-config.xml de sorte que vous n'ayez pas besoin de 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 comme étant les propriétés action et actionParam du ForwardBean .

créer une petite vue forward.xhtml (si petit qu'il s'adapte dans le tampon de réponse par défaut (souvent 2KB) de sorte qu'il peut être réinitialisé par le gestionnaire de navigationhandler, sinon vous devez augmenter le tampon de réponse dans la configuration du servletcontainer), qui invoque une méthode fève 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 <redirect /> entrées 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 est d'utiliser forward.xhtml comme

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

et mise à jour de la méthode navigate() à invoquer sur @PostConstruct (qui sera invoquée après la construction de bean et tout le cadre de propriété gérée):

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

il a le même effet, mais le côté de la vue n'est pas vraiment auto-documentée. Tout ce qu'il fait essentiellement est l'impression ForwardBean#toString() (et par la présente implicitement la construction de la fève si elle n'est pas présente encore.)


Note pour les utilisateurs de JSF2, il existe une façon plus propre de passer les paramètres avec <f:viewParam> et une façon plus robuste de gérer la redirection/navigation par <f:event type="preRenderView"> . Voir aussi entre autres:

29
répondu BalusC 2017-05-23 12:09:37
FacesContext context = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse)context.getExternalContext().getResponse();
response.sendRedirect("somePage.jsp");
5
répondu ycnix 2010-10-27 12:30:59

vous devez utiliser action au lieu de actionListener:

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

et en étroite méthode que vous avez droit à quelque chose comme:

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

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

bien sûr, toute cette portée doit être écrite dans notre page d'origine, pas dans l'intermédiaire. Et dans la méthode close() vous pouvez utiliser les paramètres pour choisir dynamiquement où rediriger.

2
répondu Vladimir Ivanov 2015-04-20 19:08:29

Edit 2

j'ai finalement trouvé une solution en mettant en œuvre mon action prospective comme cela:

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 (au 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 pas travail. En effet, lorsque la fonction doForward() est appelée, la Le cycle de vie du JSF a déjà commencé, et il n'est pas possible de recréer une nouvelle demande.


une idée pour résoudre ce problème, mais je ne l'aime pas vraiment, est de forcer l'action doForward() au cours de l'un des setBindedInputHidden() méthode:

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 pas un appel Javascript, et œuvres ne pas de travail.

1
répondu romaintaz 2010-11-04 16:56:45