ユーザーを特定のページ(アイテムを表示または編集できるページなど)に直接送信できるWebアプリケーションがあります。それを実現するために、特定のURLを提供します。これらのURLは、外部現在のWebアプリケーションに配置されます(つまり、別のWebアプリケーションまたは電子メールに存在できます)。
URLはhttp://myserver/my-app/forward.jsf?action=XXX¶m=YYY
のようになります。ここで、
from-outcome
のnavigation-case
にあるJSFアクションのfaces-config.xml
と見なすことができます。たとえば、次のようなURLを使用できます。
http://myserver/my-app/forward.jsf?action=viewItem&actionParam=1234
http://myserver/my-app/forward.jsf?action=editItem&actionParam=1234
もちろん、私はJavaクラス(bean)を使用して、いくつかのセキュリティ制約をチェックし(つまり、対応するアイテムを表示/編集することをユーザーに許可しますか))、ユーザーを正しいページにリダイレクトします(edit.xhtml
、view.xhtml
、access-denied.xhtml
など)。
現在の実装
現在、私たちは前進を達成する基本的な方法を持っています。ユーザーがリンクをクリックすると、次のXHTMLページが呼び出されます。
<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>
ご覧のとおり、2つの<h:inputHidden>
コンポーネントをmy Java Bean。にバインドします。これらはaction
およびactionParam
リクエストの両方の値を格納するために使用されますパラメータ(FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("actiontParam");
を使用)。また、ページがレンダリングされるとすぐに呼び出されるdoForward
メソッドを提供し、ユーザーを(再び)実際のページにリダイレクトします。
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);
}
このソリューションは機能していますが、それには2つの問題があります。
だから私の質問はこのリダイレクション/フォワード機能をリファクタリングする方法?
技術情報
Java 1.6、JSF 1.2、Facelets、Richfaces
手動で収集する必要がないように、faces-config.xml
でGETクエリパラメーターを管理プロパティとして設定します。
<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>
このように、リクエストforward.jsf?action=outcome1&actionParam=123
は、JSFがaction
およびactionParam
パラメーターをaction
のactionParam
およびForwardBean
プロパティとして設定できるようにします。
Beanメソッドを呼び出す小さなビューforward.xhtml
(ナビゲーションハンドラーによってリセットできるようにデフォルトの応答バッファー(多くの場合2KB)に収まるほど小さい)を作成します。そうしないと、サーブレットコンテナーの構成で応答バッファーを増やす必要があります。 f:view
のbeforePhase
:
<!DOCTYPE html>
<html xmlns:f="http://Java.Sun.com/jsf/core">
<f:view beforePhase="#{forward.navigate}" />
</html>
ForwardBean
は次のようになります。
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.
}
navigation-rule
はそれ自身を表しています(表紙の下ではExternalContext#redirect()
の代わりにExternalContext#dispatch()
を行う<redirect />
エントリに注意してください):
<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>
別の方法は、forward.xhtml
を次のように使用することです
<!DOCTYPE html>
<html>#{forward}</html>
@PostConstruct
で呼び出されるnavigate()
メソッドを更新します(Beanの構築とすべての管理プロパティ設定の後に呼び出されます)。
@PostConstruct
public void navigate() {
// ...
}
同じ効果がありますが、ビュー側は実際には自己文書化されていません。基本的には、ForwardBean#toString()
を印刷するだけです(まだ存在しない場合は、暗黙的にBeanを構築します)。
JSF2ユーザーへの注意:<f:viewParam>
でパラメーターを渡すよりクリーンな方法と、<f:event type="preRenderView">
によるリダイレクト/ナビゲーションを処理するより堅牢な方法があります。特に以下も参照してください。
FacesContext context = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse)context.getExternalContext().getResponse();
response.sendRedirect("somePage.jsp");
actionListenerの代わりにactionを使用する必要があります。
_<h:commandLink id="close" action="#{bean.close}" value="Close" immediate="true"
/>
_
そして、近い方法であなたは次のようなものを正しい:
_public String close() {
return "index?faces-redirect=true";
}
_
ここで、indexはページの1つです(index.xhtml)
もちろん、このスタッフはすべて、中級者ではなく、元のページに書く必要があります。 close()
メソッド内では、パラメーターを使用して、リダイレクト先を動的に選択できます。
編集2
私はそのような私の前方行動を実装することで最終的に解決策を見つけました:
_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();
}
}
}
}
_
(少なくとも最初のテストに関しては)動作しますが、それが実装される方法がまだ好きではありません...より良いアイデアはありますか?
編集このソリューションはnotを行います。実際、doForward()
関数が呼び出されたとき、JSFライフサイクルはすでに開始されており、新しい要求を再作成することはできません。
この問題を解決するための1つのアイデアですが、私は本当に好きではありませんが、doForward()
メソッドのいずれかで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);
}
_
このソリューションにはJavascript呼び出しは含まれません。 働く notは機能します。
Foo.jspがjspファイルであると仮定します。次のコードは、リダイレクトするボタンです。
<h:commandButton value="Redirect" action="#{trial.enter }"/>
そして、Java(service)クラスで指示するためのメソッドをチェックします
public String enter() {
if (userName.equals("xyz") && password.equals("123")) {
return "enter";
} else {
return null;
}
}
そして今、これは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>