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¶m=YYY
, Où:
from-outcome
De toute action JSF dans navigation-case
Dans faces-config.xml
.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:
Donc ma question est comment refactoriser cette fonctionnalité de redirection/transfert?
Informations techniques
Java 1.6, JSF 1.2, Facelets, Richfaces
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:
FacesContext context = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse)context.getExternalContext().getResponse();
response.sendRedirect("somePage.jsp");
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.
É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 .
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>