Webサイトに入力機能を追加する (4)

前回までで入力された試合情報に形式的な問題はないことが確認されたので、データベースに記録する情報をユーザーに提示して確認を求めることにします。

public void doPost(HttpServletRequest req, HttpServletResponse resp)
    throws IOException, ServletException
{

  MatchDAO match_dao = new MatchDAO();

  try {

    req.setCharacterEncoding("UTF-8");

    resp.setContentType("text/html; charset=UTF-8");

    // リクエストパラメータの検査

    Match new_match = validateRequestParameters(req);

    new_match.checkDuplication();

    // 登録内容を確認し、登録ページに移行する

    PrintWriter out = resp.getWriter();

    out.println("<html>");

    out.println(" <head>");

    out.println(" <title>servlet test</title>");

    out.println(" <meta charset='UTF-8' />");

    out.println(" </head>");

    out.println(" <body>");

    out.println(" <h1>新規試合情報登録</h1>");

    out.println(" <p>以下の試合情報を登録します。問題なければ下の「登録」ボタンをクリックしてください</p>");

    out.println(" <p>間違いがある場合は<a href='matches_edit.jsp'>こちら</a>から編集画面に戻れます</p>");

    out.println(" <hr />");

    out.println(" <table border=1>");

    out.println(" <tr>");

    out.println(" <th>節</th>");

    out.println(" <th>日付</th>");

    out.println(" <th>ホーム</th>");

    out.println(" <th>結果</th>");

    out.println(" <th>アウェイ</th>");

    out.println(" </tr>");

    out.println(" <tr>");

    out.println(" <td>" + new_match.getSection() + "</td>");

    out.println(" <td>" + new_match.getDate().format(date_format) + "</td>");

    out.println(" <td>" + new_match.getHome() + "</td>");

    out.println(" <td>" + new_match.getGoalsFor() + "&nbsp;-&nbsp;" + new_match.getGoalsAgainst() + "</td>");

    out.println(" <td>" + new_match.getAway() + "</td>");

    out.println(" </tr>");

    out.println(" </table>");

    out.println(" <dim style='text-align: start;'>");

    out.println(" <form method='post' action='register_match'/>");

    out.println(" <input type='submit' value='登録' />");

    out.println(" </form>");

    out.println(" </dim>");

    out.println(" </body>");

    out.println("</html>");

    out.close();

  } catch (InvalidMatchParameterException ex) {

    // 以下の例外処理はとりあえず省略

  }

}

さて、枠組みとしては以上ですが、上のコードを確認していただくとわかるとおり「登録」ボタンをクリックした際にデータベースに登録すべき試合情報がregister_matchのページには渡されません。

登録すべき試合情報をregister_matchのページに受け渡すには、以前の回の「変更」ボタンのところで示したようにtype="hidden"のinput要素を使って値を渡す方法が考えられます。

しかし、このやり方ではリクエストパラメータが改変される、あるいは不正なリクエストパラメータが渡される可能性があり、強力な性善説を前提にしない限り使い物になりません。

TomcatなどのWebコンテナにはこのような場合にページを超えてデータを受け渡す仕組みとして、セッションが用意されているので、ここではセッションを使用することにします。

セッションの使い方は割と簡単で、HttpServletRequestのgetSessionメソッドでセッションを取り出して、setAttributeメソッドを使って受け渡すデータの名前と受け渡すデータを保存するだけです。

受け取る側ではセッションのgetAttributeメソッドにデータの名前を指定することでデータを取り出すことができます。

ここではとりあえずセッションにデータを書き込む部分だけを作ります。

public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException
{

  MatchDAO match_dao = new MatchDAO();

  try {

    request.setCharacterEncoding("UTF-8");

    response.setContentType("text/html; charset=UTF-8");

    // リクエストパラメータの検査

    Match new_match = validateRequestParameters(request);

    new_match.checkDuplication();

    HttpSession session = request.getSession(true);

    session.setAttribute("match", new_match);

    // 登録内容を確認し、登録ページに移行する

    PrintWriter out = response.getWriter();

    out.println("<html>");

    // 以下略

getSessionメソッドの引数はセッションが作られていない場合に新しいセッションを作成するかを指示するもので、ここでは値を保存するのに必ずセッションが必要なのでtrueを指定しています。

setAttributeで指定するデータの名前は任意の文字列で構わないのですが、どこかで一括定義して文字列定数値を使用するのが間違いを避けるという点で望ましいと思います。

一応セッションからデータを受け取る側も書いておきます。

Match match = null;
HttpSession session = request.getSession(false);
if (session == null || (match = (Match)session.getAttribute("match")) == null) {

  // セッションに試合情報データが登録されていないのでエラーにする

}

// 以下、試合情報matchを使用して処理を行う

ここまでで大枠ができましたが、問題は表示に使用するHTMLデータがコードで大きな位置を占めるため、非常にコードの見通しが悪いところです。

HTMLデータを生成するコードを別メソッドに追い出して見通しをよくするという手法も考えられますが、ここではリダイレクトによるJSPの利用というのをやってみたいと思います。

まず、検証結果を表示するためのadd_match.jspというのを作ります。

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="standings.Match" %>

<%@ page import="java.time.format.DateTimeFormatter" %>

<jsp:useBean id="match" scope="session" type="standings.Match" />

<%!

static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd");

%>

<!DOCTYPE html>

<html>

 <head>

  <meta charset="UTF-8">

  <title>Add New Match Result</title>

 </head>

 <body>

  <h1>新規試合情報登録</h1>

  <p>以下の試合情報を登録します。問題がなければ下の「登録」ボタンをクリックしてください</p>

  <p>間違いがある場合は<a href="matches_edit.jsp">こちら</a>から編集画面に戻れます</p>

  <hr />

  <table border = 1>

   <tr>

    <th>節</th>

    <th>日付</th>

    <th>ホーム</th>

    <th>結果</th>

    <th>アウェイ</th>

   </tr>

   <tr>

    <td><%= match.getSection() %></td>

    <td><%= match.getDate().format(formatter) %></td>

    <td><%= match.getHome() %>

    <td><%= match.getGoalsFor() %>&nbsp;-&nbsp;<%= match.getGoalsAgainst() %></td>

    <td><%= match.getAway() %></td>

   </tr>

  </table>

  <form method="post" action="register_match" >

   <input type="submit" value="登録" />

  </form>

 </body>

</html>

1行目はお約束ですので、とりあえずこのまま書きます。

2行目の<%@ page import ="standings.Match" %>はセッションでのデータ受け渡しに使用するクラスのインポートを宣言しています。

ここでクラスにはパッケージ名が必須のようですので、必ずパッケージを用意しなければなりません。

パッケージはソースファイル名の先頭にpackage standings;のようにパッケージ名を指定する行を1行追加するだけです。

次の行は日付の表示形式を指定するために使用するDateTimeFormatterクラスをインポートしています。

その次<jsp:useBean id="match" scope="session" type="standings.Match" />はセッションに保存されているmatchというアトリビュートを使用する宣言で、これを書いておくことでセッションからgetAttributeメソッドを使用してデータを取り出す処理が不要になります。

あとはHTMLデータで、データ中に<%= ... %>を使用してセッションを通して受け取ったmatchオブジェクトから試合情報を取得しています。

また、セッションに保存されているMatchオブジェクトは次のregister_matchにも引き続き渡されるので、ここでは特に扱いを変えていません。

add_matchで検出したエラーも同様にjspファイルを作成することで表示内容をservletから分離することができます。

ここで、今回作っているWebアプリケーションの構造を考えてみます。

画面に表示する内容はmatches.jsp、matches_edit.jsp、add_match.jspという3個のJSPファイルで実現されています。

画面からの入力はadd_match (AddMatch.java) というservletにより処理されます。

受け取った入力値の検証やデータベースとのやり取りはMatch.javaおよびMatchDAO.javaと関連するクラス群によって処理されています。

このように値の処理、入力値の処理、画面への表示を分けてアプリケーションを作成する作成方法はMVC (Model-View-Controller) モデルと呼ばれています。

アプリケーションをこのように責務に基づいて分割することで保守性と柔軟性が得られるといわれています。

その辺についてはこのアプリケーションを完成させた後で考えていきたいと思います。

0コメント

  • 1000 / 1000