【入門編】Slim3で始める!GAE/JでWebアプリケーション開発 (第3回)

残機能の開発

<記事詳細>ページのControllerとJSPの実装(続き)

  • 必要な処理の実装ReadController を以下のように修正しました。

    read.jsp

    package simplebbs.controller.bbs;
    
    import org.slim3.controller.Controller;
    import org.slim3.controller.Navigation;
    import org.slim3.util.ApplicationMessage;
    
    import simplebbs.model.bbs.Head;
    import simplebbs.service.bbs.BbsService;
    
    public class ReadController extends Controller {
    
        @Override
        public Navigation run() throws Exception {
            BbsService service = new BbsService();
            Head head = null;
            try {
                // 指定されたkeyから記事を取得
                Key key = asKey("key");
                head = service.get(key);
            }
            catch (Exception e) {
                // keyが不正な場合
            }
            // 記事が取得できなかった場合
            if (head == null) {
                // エラーメッセージをセットしてトップページへフォワード
                errors.put("message", ApplicationMessage.get("error.entry.notfound"));
                return forward(basePath);
            }
            // 記事が存在する場合はリクエストスコープへセット
            requestScope("head", head); // 記事ヘッダ
            requestScope("body", head.getBodyRef().getModel()); // 記事本文
            requestScope("commentList", head.getCommentRef().getModelList()); // コメント一覧
            return forward("read.jsp");
        }
    }
    

    ReadController では指定されたkeyから記事を取得してリクエストパラメータにセットします。記事が取得できない場合は前の画面(トップページ)に戻し、エラーメッセージを表示するようにします。

    • 記事の取得
      BbsService service = new BbsService();
      Head head = null;
      try {
          // 指定されたkeyから記事を取得
          Key key = asKey("key");
          head = service.get(key);
      }
      catch (Exception e) {
          // keyが不正な場合
      }
      

      指定された key はキー文字列のため Controller#asKey() を使ってKeyオブジェクトに変換して取得することができます。ただし、前述したようにGETパラメータはブラウザのURL欄から容易に改ざん可能なため try ~ catch文で括っています。不正な値が指定されてきた場合は asKey() で IllegalArgumentException がスローされますが、catch文節で捉えた後、 head が取得できなかった(=null)として以降の処理に続きます。

    • 記事が取得できなかった場合
      // 記事が取得できなかった場合
      if (head == null) {
          // エラーメッセージをセットしてトップページへフォワード
          errors.put("message", ApplicationMessage.get("error.entry.notfound"));
          return forward(basePath);
      }
      

      記事が取得できなかった場合、エラーメッセージを準備してトップページへフォワードします。

      エラーメッセージは ApplicationMessage.get() を使ってメッセージリソースから取得することができます。メッセージリソースとは前回のバリデーションの項でも説明した application_xx.properties のことです。以下のようにメッセージを定義しておくことで取得できるようになります。

      application_ja.properties

      validator.required={0}は必須です。
      … (省略) …
      
      error.entry.notfound=該当の記事が見つかりません。
      

      application_en.properties(英語用)にも同じキーで英文メッセージを追加しておきましょう。ちなみにメッセージリソースファイルもHOT reloadingの対象なので定義を追加しても開発サーバの再起動は不要です。

      そして取得したエラーメッセージをJSPで表示するため “message” というキーでerrorsオブジェクトにputします。

      // エラーメッセージをセットしてトップページへフォワード
      errors.put("message", ApplicationMessage.get("error.entry.notfound"));
      return forward(basePath);
      

      errorsオブジェクトも前回のバリデーションの項で出てきたErrorsオブジェクトのことです。

    • 記事が取得できた場合
      // 記事が存在する場合はリクエストスコープへセット
      requestScope("head", head); // 記事ヘッダ
      requestScope("body", head.getBodyRef().getModel()); // 記事本文
      requestScope("commentList", head.getCommentRef().getModelList()); // コメント一覧
      return forward("read.jsp");
      

      記事が取得できた場合は記事情報をリクエストスコープにセットして記事詳細画面を表示します。

      また、記事本文の body とコメント一覧の commentList は、Headからのリレーションシップで取得することができるので、上記のように記述することができます。

  • テストケースの実行ReadController の実装が終わったら保存してテストを実行してみましょう。
    ReadControllerTestの成功イメージ
    結果がグリーンになれば ReadController は完成です。画面からも挙動を確認してみましょう。先にブラウザで開いていた記事詳細画面をリロードしてみて下さい。
    ReadController作成後にブラウザから確認イメージ
    記事のタイトル、投稿者名、本文が表示されるようになりました。コメントはまだ一件も投稿されていないので表示されていません。試しにこのままブラウザのURL上の /bbs/read?key=…に続くパラメータを改ざんしてみましょう。
    GETパラメータ改ざんイメージ
    上記は key=hogehogehoge と不正な値に変更してアクセスしてみたところです。想定どおり<トップ>ページに戻されました。しかし、エラーメッセージがどこにも表示されていません。/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>
    

    ReadController でセットしているエラーメッセージを表示する記述がどこにもないため表示できていませんでした。以下の一文を index.jsp に追加します。

    …
    <body>
      <div id="page">
      <h1>SimpleBBS</h1>
      <div class="err">${errors.message}</div>
      <table>
        <thead>
    …
    

    これで errorsオブジェクトに message というキーで登録されたメッセージを表示できるようになります。再度、ブラウザをリロードしてみて下さい。


    記事が存在しないメッセージの表示イメージ

    エラーメッセージの表示も確認できました。以上で記事詳細の実装は完了です。ここまでで作成したソースは以下になります。

    • slimplebbs.controller.bbs.ReadController.java
    • slimplebbs.controller.bbs.ReadControllerTest.java
    • war/bbs/read.jsp
    • war/bbs/index.jsp (修正版)

    次は<記事編集>ページを実装していきます。