Wicket で作っているWebページを外部公開するのに、
Wicket のPage の version number 1が検索エンジンにindexされるのが嫌なので、
外す方法を調べた結果を記載します。
結果は個人的には、外すのを諦めて、
canonical タグ を使う方向で考えます。。
使用しているWicket Version
- Wicket 7.5
<dependency>
<groupId>org.apache.wicket</groupId>
<artifactId>wicket-core</artifactId>
<version>7.5.0</version>
</dependency>
同じような内容で困った人がいないのか
StackOverFlowで以下の記事を見つけました。
-
How to delete version number in url for Wicket version 6.13.0 and newer ones? - Stack Overflow
-
[Apache Wicket bookmarkable url added one additional parameter to the link, why?
内容のざっくり理解だと、
-
Wicket 1.5 であれば、
MountedMapper を 継承したMountedMapperWithoutPageComponentInfo を作ると
Version Numberは消える。 -
Wicket 6.13以上だと、
MountedMapperWithoutPageComponentInfo を作って動かすだけだと、
ページのリロードが走り続けるので、RenderStrategy を ONE_PASS_RENDER に変更する。
というような話が書いてあったので、これを試してみます。
RenderStrategyを変えてみる
何が変わるものなのか
WicketでページのURLにアクセスすると、最初のアクセスは302リダイレクトされ、その後にverion番号付きのページに遷移していますが、
あの動作が変わるようです。
RenderStrategy に、3種類あり、
正直どう動くのかRender strategies を読んでもあんまりわからなかったので、
日本語で書かれた文書を探したところ、
以下PPT資料を見つけました。
内容としては、以下のように記載されています。
ONE_PASS_RENDER
いわゆるforwardREDIRECT_TO_BUFFER
PRG (バッファに描画しリダイレクト時に返す )
sessionId + queryStringでbuffering
default動作REDIRECT_TO_RENDER
PRG (リダイレクト時に描画する )
refreshしても大丈夫
ONE_PASS_RENDER を設定してみる
Applicationクラスに以下の記述を追加します。
getRequestCycleSettings().
setRenderStrategy(RequestCycleSettings.RenderStrategy.ONE_PASS_RENDER);
この設定でアプリケーションを再起動し、Pageを表示したところ、
version number は付与されなくなりました。
302リダイレクトされなくなり、最初にアクセスしたURLのまま画面が描画されます。2
REDIRECT_TO_BUFFER を設定してみる
Applicationクラスに以下の記述を追加します。
getRequestCycleSettings().
setRenderStrategy(RequestCycleSettings.RenderStrategy.REDIRECT_TO_BUFFER);
302リダイレクトされるようになります。
REDIRECT_TO_RENDER を設定してみる
getRequestCycleSettings().
setRenderStrategy(RequestCycleSettings.RenderStrategy.REDIRECT_TO_RENDER);
Document読んだ限りだと、version number が付与されると思いましたが、
Pageを表示したところ、
version number は付与されなくなりました。
Pageを表示しただけの場合は、ONE_PASS_RENDER と同様の動作をしています。
POST送信後の挙動がONE_PASS_RENDERとは違ってきそうな気がしましたので、
そちらの動作確認をしてみます。
ONE_PASS_RENDER、REDIRECT_TO_RENDER の POST時の動作の違い
POSTで画面遷移させた時の、URLを比べてみました。
動作確認には、Wicket とりあえず画面遷移とフォームデータの受け取りをする - @//メモ
に記載されているものを拝借して使わせて頂きました。
Form から POST して画面遷移した際のURLは以下のようになりました。
-
ONE_PASS_RENDER
http://127.0.0.1:18080/festivals?25-1.IFormSubmitListener-f
-
REDIRECT_TO_RENDER
http://127.0.0.1:18080/festivals/2?10
3
ONE_PASS_RENDER は、
Render strategies に記載している通りの、の動作になっており、
REDIRECT_TO_RENDER は、version number がつく挙動になっておりました。。4
Wicket の document の 付録 にあるクラスタリング環境下での、RenderStrategyの動作から
Wicket の document の 付録に以下の記事があります。
28 Lost In Redirection With Apache Wicket (Appendix) 6.x
ざっくり以下のようなことが書いてありました。
-
クラスリング環境下での、REDIRECT_TO_BUFFERの動作の留意点
Application サーバーが複数台ある場合、ラウンドロビンでリクエストを割り当てていると、
REDIRECT_TO_BUFFER で処理をしていると、
BufferしているAPサーバーに処理が割り当てられず、
Buffer後のResponseが取得できない場合がある。 -
留意点の解決策
ラウンドロビンで処理を振り分けたい場合は、ONE_PASS_RENDER を使う必要があるが、
Formの2重送信問題等、別の問題が発生する。
REDIRECT_TO_BUFFER でもスティッキーセッションを有効にして、おけばうまく処理が
できるようになる。5
どれを使うかの個人的な見解
以下の4つの選択肢があるとして、
-
REDIRECT_TO_BUFFER にする。
Page Version管理ができなくなるのを許容して、
MountedMapperWithoutPageComponentInfo を 使い、
且つ、
クラスタリング環境下では、スティッキーセッションを使う。 -
REDIRECT_TO_RENDER にする。
Form の Post後のリダイレクトのバージョン番号を消すため、
MountedMapperWithoutPageComponentInfoも使う。
且つ、
クラスタリング環境下では、スティッキーセッションを使う。 -
ONE_PASS_RENDER にする。
Form の 二重送信問題は、明示的にリダイレクトさせることで回避する。
クラスタリング設定は、
スティッキーセッションを使うでもラウンドロビンでもどちらでもOK。 -
Version番号を消すことは、諦める。
canonical タグ を使って、URLは正規化する。
且つ、
クラスタリング環境下では、スティッキーセッションを使う。
個人的には、3、4、かで、迷い、
-
3で、実装する場合も、jsessionId は別途対策しないといけない。
-
クラスタリング設定については、スティッキーセッションで良いと思っている。
-
ONE_PASS_RENDER で、Form以外にも、対応しないといけないパターンがどれくらいあるのかで読めない。
ところから、4で実装しようかと思いました。
追記
基本的に、REDIRECT_TO_BUFFERだけど、一部ページだけ、
ONE_PASS_RENDER したいという時に、MountedMapperWithoutPageComponentInfo が使えるようです。
全体がきりかわるとばっかり思っていたので、勉強になりました。8
Wicket 6.13以上だと、NoVersionMapper
というclass 作りなさいと、StackOverFlowに記載があったので、
試しに作ってみたらうまくいきました。
Classを作り、以下のようにページマウントすると、
getRootRequestMapperAsCompound().add(new NoVersionMapper("/festivals/${id}", FestivalDetailPage.class));
REDIRECT_TO_BUFFER の設定をしていても、対象指定ページが[?0]付かなくなります。
canonical タグの設定方法について
Url (Wicket Parent 6.26.0-SNAPSHOT API) に、
org.apache.wicket.request.Url#canonical() を使うと、JessionId等の要素を削ってくれそうなので、
こちらを使用して、URLを取得、linkタグの埋め込みをやってみます。
アプリケーションのPage基底クラスに以下、実装を追加しました。
- Page基底クラスの追加記述
@Override public void renderHead(IHeaderResponse response) { // Meta canonical response.render(MetaDataHeaderItem.forLinkTag("canonical", getCanonicalUrl())); } /** * getCanonicalUrl * * @return */ protected String getCanonicalUrl() { Url url = getRequestCycle().getRequest().getUrl(); System.out.println("url.toString()=" + url.toString()); System.out.println("url.getPath() =" + url.getPath()); System.out.println("url.toString(Url.StringMode.FULL) =" + url.toString(Url.StringMode.FULL)); System.out.println("url.canonical().getPath() =" + url.getPath()); return url.canonical().getPath(); }
URL `http://127.0.0.1:18080/festivals;jsessionid=1d1cz1rff0kc917o6sna0tv22x?0
が、
画面上表示されている状態で、
HTMLの記述と、consoleへのOUTPUTは以下のようになりました。
-
HTML
<link rel="canonical" href="festivals" />
-
OUTPUT
url.toString()=festivals url.getPath() =festivals url.toString(Url.StringMode.FULL) =http://127.0.0.1:18080/festivals url.canonical().getPath() =festivals
HTMLは相対URLが出力されています。
org.apache.wicket.request.Urlのその他のURL取得メソッドにも、
jsessionid
は出力されていないため、基本つかないのかもしれません。
URLの出力が相対URLなのと、URL#toString(Url.StringMode.FULL) で取得すると、
HTTPサーバーから、Proxyする際に、Domain名が安定しなさそうに思われたので、
domain名はResourceBundleから取得、
Urlも、pageクラスのmount URLから取得するように修正しました。
- getCanonicalUrl
/** * getCanonicalUrl * * @return */ protected String getCanonicalUrl() { return ResourceBundle.getBundle("xyz.monotalk.festivals4partypeople.web.WicketApplication").getString("domainName") + "/" + RequestCycle.get() .mapUrlFor(getClass(), null) .toString(); }
Behaviorクラスを作って、PageにADDする
BehaviorクラスのrenderHead で、canonicalタグを追加している記事がありましたので、
リンクを貼っておきます。
補足. ONE_PASS_RENDER で、Form Submit時に別ページへリダイレクトさせる
ONE_PASS_RENDER で うまくバージョン番号を消す方法は、検証しきれないですが、
Form をSubmitした後、
http://127.0.0.1:18080/festivals?25-1.IFormSubmitListener-f
となるのを、http://127.0.0.1:18080/festivals/2
のバージョン番号なしの形式で、
遷移させることはできましたので、
その実装方を記載します。
- RequestCycle#setResponsePage(Class<? extends IRequestablePage> pageClass, PageParameters parameters) を使って遷移させる。
以下の通り記載したところ、
ブラウザにURLが、http://127.0.0.1:18080/festivals?25-1.IFormSubmitListener-f
で返されず、
http://127.0.0.1:18080/festivals/2
で返されるようになりました。
-
修正前
@Override protected void onSubmit() { PageParameters param = new PageParameters(); param.add("id", 2); setResponsePage(new FestivalDetailPage(param)); }
-
修正後
@Override protected void onSubmit() { PageParameters param = new PageParameters(); param.add("id", 2); setResponsePage(FestivalDetailPage.class, param); }
何故URLの描画のされ方が変わるかというと、それぞれのメソッド内で指定している
RedirectPolicy が違うためでした。
-
RequestCycle#setResponsePage(IRequestablePage page)
/** * Convenience method for setting next page to be rendered. * * @param page */ public void setResponsePage(IRequestablePage page) { if (page instanceof Page) { ((Page) page).setStatelessHint(false); } // RenderPageRequestHandler.RedirectPolicy.AUTO_REDIRECT を指定 scheduleRequestHandlerAfterCurrent(new RenderPageRequestHandler(new PageProvider(page), RenderPageRequestHandler.RedirectPolicy.AUTO_REDIRECT)); }
-
RequestCycle#setResponsePagesetResponsePage(Class<? extends IRequestablePage> pageClass, PageParameters parameters)
/** * Convenience method for setting next page to be rendered. * * @param pageClass * The class of the page to render * @param parameters * The query parameters for the page to be rendered */ public void setResponsePage(Class<? extends IRequestablePage> pageClass, PageParameters parameters) { //RenderPageRequestHandler.RedirectPolicy.ALWAYS_REDIRECT を指定 setResponsePage(pageClass, parameters, RenderPageRequestHandler.RedirectPolicy.ALWAYS_REDIRECT); }
AUTO_REDIRECT だと、条件に合致すれば、リダイレクトする。しなければフォワード。6
ALWAYS_REDIRECT だと 必ずリダイレクトされるようになります。
長くなりましたが、以上です。7
[1] URLの後ろに付与されるの?0
、とか?23
のことです。
[2] MountedMapperWithoutPageComponentInfo 作成しなくても期待している動作をします。
[3] http://127.0.0.1:18080/festivals?25-1.IFormSubmitListener-f
から302リダイレクトされてます。
[4] 個人の勝手な予想に対して、想定外の動きになっているという意味で。。
[5] アプリケーションデプロイ時のLB切り離しのケースを除くと思います。
[6] ONE_PASS_RENDER の場合は、リダイレクトの対象外になります。
[7] 似たようなメソッドで遷移の仕方が異なるのは少し解せませんが、勉強にはなりました。
コメント