JSFをちょっと便利にする仕組み
はじめに
JSFで遷移先を指定するときに、return xxx.xhtml
と記述します。
リダイレクトしたい場合は
return xxx.xhtml?faces-redirect=true
と記述します。
というか、リダイレクトしないことは皆無に近しいので、必ず記述します。
毎回追記するのも面倒だし、typoした場合、実行時例外になるので 正直面倒です。
定数にして、必ず追記するように、というのも方法としてありますが、とにかく「約束事の記述」は そもそも減らしたいです。
対応方法
Interceptor
を使って、アクションの戻り値(つまり遷移先のViewID)に強制的に付与します。
Annotation
@Stereotype @Named @RequestScoped @Target(value = {ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @InterceptorBinding public @interface Action { }
Interceptor
@Priority
の値を大きく(つまり優先度を低く)しているのは、各Interceptorの実行後で一番初めに処理してほしいからです。
つまり遷移後の状態を、他のInterceptorの実行前にリダイレクトとなるように処置しておくことで 画面遷移周りの挙動の安定を図ろうという算段です。
*1
工夫したところとしては、遷移後の画面IDに単純に?faces-redirect=true
を付与するだけだと、
自画面遷移として戻り値をvoid
にしたときにNullPointerException
になってしまうので 遷移前の画面IDを取得するようにしているところです。
@Interceptor @Dependent @Priority(Interceptor.Priority.APPLICATION + 100) @Action public class ForceRedirectInterceptor { @AroundInvoke public Object invoke(InvocationContext ic) throws Exception { String currentViewId = FacesContext.getCurrentInstance().getViewRoot().getViewId(); Object resultViewId = ic.proceed(); if (resultViewId == null) { resultViewId = currentViewId; } resultViewId += "?faces-redirect=true"; return resultViewId; } }
使用実装
リダイレクトの記述をせずとも@Action
があれば自動でリダイレクトされます。
ちょっと、手を抜きますが、実際に動かすと きちんとURLがアクションにあわせて
「今 有効となっている画面のURL」になっていることが確認できると思います。
@Action public class IndexAction { public String fwCaseOne() { return "case1/index.xhtml"; } }
Code
今回は提示なしです。 Conversationとの組み合わせと合わせて公開します。
さいごに
多くのWebフレームワークではリダイレクトをしないことがデフォルトで、「リダイレクトするときには、こうします」みたいな実装例が示されるように感じています。
でも 個人的に不思議なのが、Webシステムでの URLの表記や、2度押しなどのリスクを鑑みた場合に「リダイレクトをしない」ケースの方が稀な気がするのです。
少なくともWebアプリレベルで リダイレクトを する か しない か は決まると思うので、起動パラメータやConfigでON・OFFできるようにしたら良い気がするのですが、実際そんなフレームワークを知らないので需要は無いのかな?
リダイレクトするとき と しないとき の場合分けを仕組みとして持たせたい要望があれば、System Property とかで指定できるようにしようかなぁと思いますが、
自分がそのケースに遭遇する気がしないので、私は「リダイレクトは常にする」を強制するという、今回のInterceptor
を標準的に適用しようと思っています。
*1:これは、今後書く予定のConversationScopedの自動endに関連するので、その時に生きてきますが、それはまた後日ということで