クラウドアプリケーション構築 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エスケープ(クロスサイトスクリプティング対策で”<“を”&lt;”などに変換すること)を行います。

      ${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の<トップ>ページが表示されているはずです。


    index.jsp修正後画面イメージ

    次に headList をリクエストスコープにセットし、記事一覧を表示できるように IndexController を修正します。

  • テストケースの作成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テストを実行してみて下さい。結果は失敗になるはずです。


    IndexControllerTest修正後の実行失敗イメージ

    次に、このテストをクリアするよう 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修正後のテスト実行イメージ
    グリーンになっていれば IndexController の実装は完了です。念のため、画面からの確認も行って見ます。先にブラウザで開いていた http://localhost:8888/bbs/ を再読み込みします。
    IndexController修正後の画面イメージ
    再読み込みしても表示は何も変わりませんが、これはまだ一件も記事が投稿されていないからです。ここまでで作成したソースは以下になります。

    • simplebbs.controller.bbs.IndexController.java
    • simplebbs.controller.bbs.IndexControllerTest.java
    • war/bbs/index.jsp
    • war/css/bbs.css

    次に、新しい記事を投稿する<記事作成>ページを実装しましょう。