JSF 1.x でページ読み込み時にリダイレクトを行う方法
質問
ユーザーをいくつかの特定のページ (項目を表示または編集できるページなど) に直接送信できる Web アプリケーションがあります。これを実現するために、特定の URL を提供します。これらの URL は次のとおりです。 外 現在の Web アプリケーション (つまり、別の Web アプリケーションまたは電子メールに存在する可能性があります)。
URLは次のようになります http://myserver/my-app/forward.jsf?action=XXX¶m=YYY
, 、 どこ:
- アクション ユーザーがリダイレクトされるページを表します。これは次のように考えることができます。
from-outcome
の JSF アクションのnavigation-case
でfaces-config.xml
. - アクションパラメータ 前のアクションのパラメータ (通常はアイテム ID)
たとえば、次のような 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>
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 つの問題があります。
- 私はその実装方法が好きではありません。もっと単純なもの(サーブレットを使用する?)ができると確信しています。
- このソリューションは Javascript を使用していますが、JavaScript を使用してはなりません (このフォワードは、JavaScript がサポートされていない古い Blackberry ユーザーによって使用される可能性があるため)。
それで私の質問は このリダイレクト/転送機能をリファクタリングするにはどうすればよいですか?
技術的な案内
Java 1.6、JSF 1.2、Facelets、Richfaces
解決
GET クエリ パラメーターを管理プロパティとして設定します。 faces-config.xml
手動で収集する必要がないようにします。
<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
.
小さなビューを作成する forward.xhtml
(デフォルトの応答バッファ (多くの場合 2KB) に収まるほど小さいので、ナビゲーションハンドラによってリセットできます。そうでない場合は、サーブレットコンテナの設定で応答バッファを増やす必要があります)。これにより、Bean メソッドが呼び出されます。 beforePhase
の f:view
:
<!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
それ自体が物語っています(注意してください) <redirect />
実行するエントリ ExternalContext#redirect()
の代わりに ExternalContext#dispatch()
カバーの下に):
<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>
そして更新します navigate()
呼び出されるメソッド @PostConstruct
(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"
/>
そして、close メソッドでは、次のように修正します。
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();
}
}
}
}
(少なくとも最初のテストに関しては) 機能しますが、実装方法がまだ気に入りません...もっと良いアイデアはありますか?
編集 この解決策は、 ない 仕事。確かに、 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 呼び出しは含まれていません。 作品 する ない 仕事。
foo.jsp が jsp ファイルであると仮定します。次のコードはリダイレクトしたいボタンです。
<h:commandButton value="Redirect" action="#{trial.enter }"/>
次に、Java (サービス) クラスでのダイレクトのメソッドを確認します。
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>