Domanda

Ho un web-application in cui gli utenti possono essere inviati direttamente ad alcune pagine specifiche (ad esempio, una pagina in cui si possono visualizzare o modificare una voce). Per raggiungere questo, mettiamo a disposizione un URL specifico. Questi URL sono situati fuori l'attuale applicazione web (cioè esse possono essere presenti in un altro sito-applicazione, o in un'email).

L'aspetto URL del tipo http://myserver/my-app/forward.jsf?action=XXX&param=YYY, dove:

  • azione rappresenta la pagina in cui verrà reindirizzato l'utente. Si può considerare questo come il from-outcome di qualsiasi azione JSF in navigation-case in faces-config.xml.
  • actionParam è un parametro per l'azione precedente (in genere un ID articolo)

Così, per esempio, posso avere questo tipo di URL:

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

Naturalmente, ho una classe Java (di semi) che controllerà alcuni vincoli di sicurezza (vale a dire è che l'utente permesso di vedere / modificare la voce corrispondente?) E quindi reindirizzare l'utente alla pagina corretta (come edit.xhtml, view.xhtml o access-denied.xhtml).


implementazione corrente

Al momento, abbiamo un modo di base per realizzare l'attaccante. Quando l'utente clicca sul link, la seguente pagina XHTML è chiamato:

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

Come si può vedere, mi legano due componenti <h:inputHidden> nel mio bean Java. Essi saranno utilizzati per memorizzare il valore di entrambi parametro action e actionParam richiesta (usando FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("actiontParam");). Ho anche fornire il metodo doForward quella che verrà chiamato immediatamente quando la pagina viene visualizzata, che reindirizzare (nuovo) l'utente alla pagina reale. Il metodo è il seguente:

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

Questa soluzione funziona, ma ho due problemi con questo:

  • Non mi piace il modo in cui viene attuata. Sono sicuro che posso avere qualcosa di più semplice (utilizzando una Servlet?).
  • Questa soluzione utilizza Javascript, e non deve utilizzare Javascript (come questo in avanti può essere utilizzato da vecchi utenti Blackberry, dove il Javascript non è supportato).

Quindi la mia domanda è come il refactoring il reindirizzamento / funzione in avanti?

Le informazioni tecniche

Java 1.6, JSF 1.2, Facelets, RichFaces

È stato utile?

Soluzione

Impostare i parametri di query ottenere come proprietà gestite a faces-config.xml in modo che non c'è bisogno di raccogliere manualmente:

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

In questo modo la richiesta forward.jsf?action=outcome1&actionParam=123 lascerà JSF impostare i parametri action e actionParam come action e actionParam proprietà del ForwardBean.

Creare una piccola vista forward.xhtml (così piccola che si inserisce nel buffer di risposta di default (spesso 2 KB) in modo che possa essere resettato dalla navigationhandler, altrimenti hai di aumentare il buffer di risposta nella configurazione del servletcontainer), che invoca un metodo di fagiolo su beforePhase del f:view:

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

Il ForwardBean può apparire così:

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

Il navigation-rule parla da sé (notare le voci <redirect /> che fare ExternalContext#redirect() invece di ExternalContext#dispatch() sotto le coperte):

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

Un'alternativa è quella di utilizzare forward.xhtml come

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

e aggiornare il metodo navigate() da richiamare su @PostConstruct (che viene attivata dopo che la costruzione di fagioli e tutte le impostazioni proprietà gestita):

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

Ha lo stesso effetto, tuttavia la vista laterale in realtà non è auto-documentazione. Tutto ciò che fa è fondamentalmente la stampa ForwardBean#toString() (e con la presente implicitamente costruire il fagiolo se non presente ancora).


Nota per gli utenti JSF2, c'è un modo più pulito di passare i parametri con <f:viewParam> e il modo più robusto di gestire il reindirizzamento / navigazione <f:event type="preRenderView">. Vedi anche, tra gli altri:

Altri suggerimenti

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

si dovrebbe usare l'azione invece di ActionListener:

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

e in stretta metodo che qualcosa di destra come:

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

dove l'indice è una delle tue pagine (index.xhtml)

Naturalmente, tutto questo personale dovrebbe essere scritto nella nostra pagina originale, non nel intermedio. E dentro il metodo close() è possibile utilizzare i parametri di scegliere dinamicamente dove reindirizzare.

Modifica 2

Finalmente ho trovato una soluzione mediante l'attuazione di mia azione in avanti in quel modo:

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

Si lavora (almeno per quanto riguarda le mie prime prove), ma ancora non mi piace il modo in cui viene attuata ... Qualche idea migliore?


Modifica Questa soluzione fa non il lavoro. In effetti, quando viene chiamata la funzione doForward(), il ciclo di vita JSF è già stato avviato, e quindi ricreare una nuova richiesta non è possibile.


Un'idea per risolvere questo problema, ma non mi piace, è quello di forzare l'azione doForward() durante uno dei metodi 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);
}

Questa soluzione non comporta alcuna chiamata Javascript, e lavori ha non di lavoro.

Si supponga che foo.jsp è il file jsp. e codice seguente è il pulsante che si desidera fare redirect.

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

E ora controlleremo il metodo per indirizzare in java (servizio) di classe

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

e ora questo è una parte del file 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>
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top