システム開発で思うところ

Javaで主にシステム開発をしながら思うところをツラツラを綴る

JSFで自動Redirect

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に関連するので、その時に生きてきますが、それはまた後日ということで