クラウドアプリケーション構築 Slim3でGAE/J(第2回) 2/5ページ
Controllerの作成
<トップ>ページのControllerとJSPの実装
SimpleBBSのスタートページとなる<トップ>ページから作成してみます。画面イメージをおさらいしてみましょう。
<トップ>ページには”記事一覧”と”新規記事作成リンク”があります。記事一覧に表示するデータは前回作成した BbsService クラスを使います。
ControllerとJSPはどちらから実装してもよいですが、今回はSlim3のHOT reloadingを最大限活用したいのでJSPから実装していきます。こうすることで画面表示を確認しながらControllerを実装することができます。(もちろん開発サーバーの再起動を行わずに、です)
- JSPの実装gen-controller で生成された war/bbs/index.jsp を以下のように修正しました。
index.jsp
<%@page pageEncoding="UTF-8" isELIgnored="false" session="false"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> <%@taglib prefix="f" uri="http://www.slim3.org/functions"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>SimpleBBS</title> <link href="/css/bbs.css" rel="stylesheet" type="text/css" /> </head> <body> <div id="page"> <h1>SimpleBBS</h1> <table> <thead> <tr> <td class="subject">タイトル</td> <td class="username">投稿者</td> <td class="post_date">投稿日時</td> <td class="last_comment_id">コメント数</td> <td class="last_comment_date">最終コメント日時</td> </tr> </thead> <tbody> <c:forEach var="head" items="${headList}" varStatus="hs" > <tr> <td><a href="read?key=${f:h(head.key)}">${f:h(head.subject)}</a></td> <td>${f:h(head.username)}</td> <td><fmt:formatDate value="${head.postDate}" pattern="yyyy/MM/dd HH:mm:ss" /></td> <td>${head.lastCommentId}</td> <td><fmt:formatDate value="${head.lastCommentDate}" pattern="yyyy/MM/dd HH:mm:ss" /></td> </tr> </c:forEach> </tbody> </table> <p><a href="create">新しい記事を投稿する</a></p> </div> </body> </html>
- 記事一覧についてindex.jsp で記事一覧を表示しているのは以下のループ処理です。
<c:forEach var="head" items="${headList}" varStatus="hs" > … </c:forEach>
Controllerによってリクエストスコープにセットされる headList (HeadのListオブジェクト)をループして、記事一覧をテーブルに出力しています。
-
f:h() について
EL(式言語。${…}で記述された部分)に f:h(…) が頻繁に記述されていますが、これはSlim3独自のJSP Functionです。次のようにtaglibディレクティブに記述することで利用できるようになります。
<%@taglib prefix="f" uri="http://www.slim3.org/functions"%>
そして f:h() には二つの機能があります。一つ目は文字列を引数に指定すると、HTMLエスケープ(クロスサイトスクリプティング対策で”<“を”<”などに変換すること)を行います。
${f:h(head.subject)}
記事のタイトルや本文、名前などのようにユーザの入力値を直接画面表示する場合には必須の処理です。二つ目の機能はKeyオブジェクトを引数に指定すると、キー文字列に変換を行います。
<a href="read?key=${f:h(head.key)}">…</a>
元々、Keyはリクエストパラメータに含められるようKeyから文字列へ(KeyFactory.keyToString())、文字列からKeyへ(KeyFactory.stringToKey())の変換が行えるようになっていますが、 f:h() のおかげでJSPからModelオブジェクトのKeyプロパティを出力し易くなります。
それ以外のtaglibはJSTLのものなので、ここでは説明を省きます。
- 新規記事作成リンクについて記事作成ページへのリンクです。
<a href="create">新しい記事を投稿する</a>
href に指定するパスは / で始まるパスはコンテキストルートからみたパスになり、 / で始まらないパスはJSPからみたパスになります。上記の場合はJSPから見たパスなので /bbs/create へのリンクになります。
- デザインについてHTMLタグ内でidやclass指定しているところがありますが、これはCSSで見栄えを良くするためだけに利用しています。この記述はなくても処理上は問題ありません。
JSPの修正(とCSSの配置)が終わったら保存して、先にブラウザで開いていた http://localhost:8888/bbs/ を再読み込みします。すると、先ほどの”Hello bbs Index !!!”ではなく、SimpleBBSの<トップ>ページが表示されているはずです。
次に headList をリクエストスコープにセットし、記事一覧を表示できるように IndexController を修正します。
- 記事一覧についてindex.jsp で記事一覧を表示しているのは以下のループ処理です。
- テストケースの作成Controllerの実装は例によって先にテストケースを作成します。gen-controller にて生成された IndexControllerTest#run()メソッドを以下のように修正しました。青字が手を加えた箇所になります。
IndexControllerTest.java
@Test public void run() throws Exception { tester.start("/bbs/"); IndexController controller = tester.getController(); assertThat(controller, is(notNullValue())); assertThat(tester.isRedirect(), is(false)); assertThat(tester.getDestinationPath(), is("/bbs/index.jsp")); assertThat(tester.requestScope("headList"), is(notNullValue())); }
追加したテストコードは、 IndexController の処理後、リクエストスコープに headList が設定されていることを検査しています。tester.requestScope(“headList”) は tester.request.getAttribute(“headList”) を省略したメソッドです。試しにこの状態でJUnitテストを実行してみて下さい。結果は失敗になるはずです。
次に、このテストをクリアするよう IndexController を修正します。
- 必要な処理の実装IndexController を以下のように修正しました。青字が手を加えた箇所になります。
IndexController.java
package simplebbs.controller.bbs; import java.util.List; import org.slim3.controller.Controller; import org.slim3.controller.Navigation; import simplebbs.model.bbs.Head; import simplebbs.service.bbs.BbsService; public class IndexController extends Controller { @Override public Navigation run() throws Exception { BbsService service = new BbsService(); List<Head> headList = service.getAll(); requestScope("headList", headList); return forward("index.jsp"); } }
BbsServiceは前回作成したもので、 BbsService#getAll() は記事を全件取得するメソッドです。この結果を ”headList” というネームでリクエストスコープにセットして、先のJSPで扱えるようにしています。
requestScope(…) はテストクラスでも出てきましたが、 HttpServletRequest#getAttribute()、setAttribute()を簡単に記述できるものです。特にgetする際は、キャストを記述しなくて済むのでソースの見通しが良くなります。今回は使いませんが、同じように sessionScope() や applicationScope() もあります。
これで IndexController の修正は終わりました。保存して IndexControllerTest を再度実行してみましょう。
- テストケースの実行
グリーンになっていれば IndexController の実装は完了です。念のため、画面からの確認も行って見ます。先にブラウザで開いていた http://localhost:8888/bbs/ を再読み込みします。
再読み込みしても表示は何も変わりませんが、これはまだ一件も記事が投稿されていないからです。ここまでで作成したソースは以下になります。- simplebbs.controller.bbs.IndexController.java
- simplebbs.controller.bbs.IndexControllerTest.java
- war/bbs/index.jsp
- war/css/bbs.css
次に、新しい記事を投稿する<記事作成>ページを実装しましょう。