第8章.オブジェクトのスコープ -リクエストとセッション

ネットワークプログラミング論 平成 27 年 11 月 23 日
第8章.オブジェクトのスコープ -リクエストとセッション
【学習のねらい】
① フォワード(forward)によってサーブレットや JSP 間でデータの受け渡しを行う際、
受け渡しが可能な範囲(スコープ)があることを理解する。
② リクエストスコープとセッションスコープの違いを、実例を通じて理解する。
<先週の復習>
講義で示された【基礎課題 8-1】に解答して下さい。
8-1.リクエスト属性の受け渡し
本章では、まず次のような Web アプリケーショ
ンを作成しましょう。
所定の JSP に接続すると、次のような画面が
現れるので、ここで、購入したい商品を選択す
る(
【基礎課題 6-7】で作成した JSP と同様)
。
選択後、
[送信]ボタンをクリックする。
すると、今度は次の画面に移るので、ここ
では購入したい書籍を選択する。
[送信]ボタンクリック後、全購入品目が
まとめて表示された画面に移動する。
105
ネットワークプログラミング論 平成 27 年 11 月 23 日
この Web アプリケーションを、ここでは次のような流れ(構成)で作ります。
forward
リクエスト1
購入画面1
kounyu1.jsp
サーブレット1
(購入商品1)
KounyuServlet1.java
購入画面2
(購入商品1) kounyu2.jsp
リクエスト2
(購入商品2)
レスポンス
ブラウザ
サーブレット2
購入商品ページ
KounyuServlet2.java
この Web アプリケーションのポイントは、
「購入画面1」で指定した商品データ(購入商
品1)をサーブレット2まで保持しておくということです。そのために、サーブレット 1
を用意し、
(第 7 章で学習したように)
「購入商品1」のデータをリクエスト属性に保管し
転送(forward)しています。「購入画面1」および「購入画面2」からの入力データの受
け取りは第 6 章で学習した通りにやればできます。この方針にしたがって、次のように各
JSP およびサーブレットを作成してみましょう。
① 【基礎課題 6-7】で作成した「checkbox.jsp」を次のように修正して「kounyu1.jsp」
と別名保管してください。
<kounyu1.jsp>
<%@page contentType="text/html; charset=Windows-31J"%>
<HTML>
<BODY>
<H2>購入画面1</H2>
購入したい商品を選んで下さい。<br>
<FORM ACTION="../KounyuServlet1" METHOD="POST">
<INPUT TYPE="CHECKBOX" NAME="Shohin1" VALUE="ノート PC">ノート PC
<INPUT TYPE="CHECKBOX" NAME="Shohin1" VALUE="iPhone 6">iPhone 6
<INPUT TYPE="CHECKBOX" NAME="Shohin1" VALUE="任天堂 Wii U">
任天堂 Wii U<br><br>
<INPUT TYPE="SUBMIT" VALUE="送信">
<INPUT TYPE="RESET">
</FORM>
</BODY>
</HTML>
② 次に同じく【基礎課題 6-7】で作成した「CheckBoxServlet.java」を次のように修正し
て「KounyuServlet1.java」と別名保管してください。
106
ネットワークプログラミング論 平成 27 年 11 月 23 日
package input;
import java.io.IOException;
import java.io.PrintWriter;
import
import
import
import
import
<KounyuServlet1.java>
追加
javax.servlet.RequestDispatcher;
javax.servlet.ServletException;
javax.servlet.http.HttpServlet;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;
public class KounyuServlet1 extends HttpServlet {
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("Windows-31J");
response.setContentType("text/html;charset=Windows-31J");
(ア) String[] values1=request.getParameterValues("Shohin1");
(ウ) request.setAttribute("Shohin1", values1);
RequestDispatcher dispatcher=
(イ)
request.getRequestDispatcher("/input/kounyu2.jsp");
dispatcher.forward(request,response);
}
}
※ (ア)は p.82 の【解説】
、
(イ)は p.88、そして(ウ)は p.93 の【解説】参照
③ 「kounyu1.jsp」を次のように修正して「kounyu2.jsp」と別名保管してください。
<%@page contentType="text/html; charset=Windows-31J"%>
<HTML>
<kounyu2.jsp>
<BODY>
<H2>購入画面2</H2>
購入したい書籍を選んで下さい。<br>
<FORM ACTION="KounyuServlet2" METHOD="POST">
<INPUT TYPE="CHECKBOX" NAME="Shohin2"
VALUE="基礎からのサーブレット/JSP">基礎からのサーブレット/JSP
<INPUT TYPE="CHECKBOX" NAME="Shohin2"
VALUE="プロになるための Web 技術入門">プロになるための Web 技術入門
<INPUT TYPE="CHECKBOX" NAME="Shohin2"
VALUE="Web アプリケーションの常識">Web アプリケーションの常識<br><br>
<INPUT TYPE="SUBMIT" VALUE="送信">
<INPUT TYPE="RESET">
</FORM>
</BODY>
</HTML>
107
ネットワークプログラミング論 平成 27 年 11 月 23 日
④ 「KounyuServlet1.java」を次のように修正して「KounyuServlet2.java」と別名保管
してください。
<KounyuServlet2.java>
package input;
・・・
public class KounyuServlet2 extends HttpServlet {
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("Windows-31J");
response.setContentType("text/html;charset=Windows-31J");
PrintWriter out=response.getWriter();
out.println("<HTML>");
out.println("<HEAD><TITLE>購入商品ページ</TITLE></HEAD>");
out.println("<BODY>");
out.println("<H2>購入商品ページ</H2>");
out.println("あなたが購入した商品は以下の通りです。<br>");
out.println("<H3><コンピュータ家電機器></H3>");
String[] values1=(String[]) request.getAttribute("Shohin1");
if(values1==null) {
out.println("何も購入していません。<br>");
}
else {
for(int i=0;i<values1.length;i++) {
out.println(values1[i]+"<br>");
}
}
String[] values2=request.getParameterValues("Shohin2");
out.println("<H3><書籍></H3>");
if(values2==null) {
out.println("何も購入していません。<br>");
}
else {
for(int i=0;i<values2.length;i++) {
out.println(values2[i]+"<br>");
}
}
out.println("</BODY>" );
out.println("</HTML>" );
}
}
108
ネットワークプログラミング論 平成 27 年 11 月 23 日
⑤ 最後に「web.xml」に次のように追加し、新たに作成したサーブレットを登録してくだ
さい。
<web.xml>
<web-app>
・・・
<servlet>
<servlet-name>KounyuServlet1</servlet-name>
<servlet-class>input.KounyuServlet1</servlet-class>
</servlet>
<servlet>
<servlet-name>KounyuServlet2</servlet-name>
<servlet-class>input.KounyuServlet2</servlet-class>
</servlet>
・・・
<servlet-mapping>
<servlet-name>KounyuServlet1</servlet-name>
<url-pattern>/KounyuServlet1</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>KounyuServlet2</servlet-name>
<url-pattern>/KounyuServlet2</url-pattern>
</servlet-mapping>
</web-app>
作成したら、p.105 のように商品を選択して「購入商品ページ」を確認してください。う
まく表示されたでしょうか・・・?
実は、表示結果は次のようになり、購入画面1で選択
した商品は表示されないはずです。
この原因はサーブレット1(KounyuServlet1.java)
の(ウ)の部分
request.setAttribute("Shohin1", values1);
によってリクエスト属性として保管した「購入商品1」
の情報が、サーブレット2(KounyuServlet2.java)ま
で届かないことにあります。
この事情を説明しましょう。実は、リクエスト属性は
1 回のリクエストの範囲で有効で、その次に新たなリクエストがあると、内容は消えてしま
います。そのため、次の図に示すように「サーブレット1」で保管したリクエスト属性は
「購入画面2」までは有効ですが、リクエスト2が発生した段階で消えてしまうのです。
109
ネットワークプログラミング論 平成 27 年 11 月 23 日
<リクエスト属性の有効範囲(スコープ)>
リクエスト属性(購入商品1)は有効
forward
リクエスト1
購入画面1
kounyu1.jsp
サーブレット1
(購入商品1)
KounyuServlet1.java
購入画面2
(購入商品1) kounyu2.jsp
リクエスト2
(購入商品2)
リクエスト2が発生した段階で、これ
以前のリクエスト属性は破棄される。
レスポンス
ブラウザ
サーブレット2
購入商品ページ
KounyuServlet2.java
このような有効範囲をスコープと言います。リクエストのスコープは、1 回のリクエスト
の範囲です。今の場合は、複数のリクエストをまたいで有効な属性が必要になります。そ
れについて次節で学習しましょう。
【基礎課題 8-2】
上の動作を確認してください。購入画面1で指定した商品の情報が残らないことを確認
したら、
「リクエスト属性を用いた場合、リクエストをまたいでデータを伝えられないこと
を確認しました。
」と記述して提出してください。
8-2.セッション属性の利用
リクエストよりもスコープの広いものとして、セッションがあります。このセッション
に保管したデータつまりセッション属性は、ブラウザを閉じるまで有効です。セッション
はセッション ID によって管理されます。あるユーザ(クライアント)が Web アプリケー
ションに接続した際に、セッション ID が割り振られます。このセッション ID はクライア
ントがブラウザを閉じるまで(ユーザ認証している時にはログアウトするまで)有効なの
で、その間はセッション属性が保持されるようになっています。
それでは、以下の手順にしたがって、前節のサーブレットを改良しましょう。変更点は
リクエストをセッションに変更する部分のみです。
① 「KounyuServlet1.java」を次のように修正してください。下線部が修正箇所です。
110
ネットワークプログラミング論 平成 27 年 11 月 23 日
<KounyuServlet1.java>
package input;
HttpSession クラスをインポートする
・・・
import javax.servlet.http.HttpSession;
public class KounyuServlet1 extends HttpServlet {
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("Windows-31J");
response.setContentType("text/html;charset=Windows-31J");
String[] values1=request.getParameterValues("Shohin1");
これで、セッションオブジェクトを生成する
HttpSession session=request.getSession();
セッション属性への保管の仕方はリクエスト属性と同じ
session.setAttribute("Shohin1", values1);
request.setAttribute("Shohin1", values1);
RequestDispatcher dispatcher=
request.getRequestDispatcher("/input/kounyu2.jsp");
dispatcher.forward(request,response);
}
}
② 次に「KounyuServlet2.java」を次のように修正してください。下線部が修正箇所です。
package input;
・・・
import javax.servlet.http.HttpSession;
public class KounyuServlet2 extends HttpServlet {
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("Windows-31J");
・・・
out.println("<H3><コンピュータ家電機器></H3>");
HttpSession session=request.getSession();
String[] values1=(String[]) session.getAttribute("Shohin1");
String[] values1=(String[]) request.getAttribute("Shohin1");
if(values1==null) {
out.println("何も購入していません。<br>");
}
・・・
}
}
111
ネットワークプログラミング論 平成 27 年 11 月 23 日
【基礎課題 8-3】
作成したら表示結果を確認して下さい。表示結果を確認したら、
「セッション属性を用い
る事で、購入画面1でチェックした商品もきちんと表示されることを確認しました。
」と記
述して提出して下さい。
【基礎課題 8-4】
ブラウザを閉じるまで、セッション属性が有効であることを改めて確認してみましょう。
① まず、
「購入画面 1」に接続します。ここで、適当に商品を選んで[送信]ボタンをク
リックします。
② この後、
「購入画面2」へ進みます。しかし、ここではまだ何も選択しませず他の Web
アプリケーションに接続してみます。
③ 例えば、
【基礎課題 6-3】で作成した JSP(input.jsp)へ接続してください。そして、
次のように氏名を入力し「送信」ボタンをクリックします。
112
ネットワークプログラミング論 平成 27 年 11 月 23 日
④ すると、次のように表示されます。
⑤ ここで、
[戻る]ボタンを使って、②の「購入画面2」まで戻ってください。ここで、
適当に商品(書籍)を選び[送信]ボタンをクリックします。
⑥ すると、①で指定した商品もきちんと表示されます。
上の動作を確認したら、
「動作の途中で他の Web アプリケーションを動作させても、セッ
ション属性として保管したデータが保持されることを確認しました。
」と記述して提出して
下さい。
113
ネットワークプログラミング論 平成 27 年 11 月 23 日
8-3.応用課題
【応用課題 8-A】
セッション属性を利用して次のような、訪問回数をカウントする Web アプリケーション
を作成しましょう。
初めて接続した時には左のように表示され
る。
以降は、ブラウザをリロード(更新)する
度に 1 回ずつ訪問回数が増えて表示され
る。
このサーブレットの作成を「KounyuServlet2.java」を修正することで行いましょう。
「KounyuServlet2.java」を次ページのように修正して「SessionCounterServlet.java」と
別名保管してください。また、
「web.xml」にそのサーブレットを登録してください。
作成したら表示結果を確認して下さい。表示結果を確認したら、
「更新(リロード)する
毎に、訪問回数が増えることを確認しました。」と記述して提出して下さい。
【基礎課題 8-B】
次の実験を行ってください。
① ブラウザを二つ起動させる(ブラウザ A、ブラウザBとする)。同種であればインター
ネットエクスプローラや FireFox など何でも構いません。
② ブラウザ A で上のサーブレットに接続して訪問回数が3となるまでリロード(更新)
する。
③ その後、もう一方のブラウザBで同じく上のサーブレットに接続する。
このとき、ブラウザBの訪問回数は何になっていますか?その解答を、使用したブラウ
ザの種類と共に記述して提出してください。
114
ネットワークプログラミング論 平成 27 年 11 月 23 日
<SessionCounterServlet.java>
package input;
・・・
ブラウザをリロード(更新)した時には GET リクエストが発生する
ので、
(doPost()ではなく)doGet()メソッドで受けます。
public class SessionCounterServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("Windows-31J");
response.setContentType("text/html;charset=Windows-31J");
PrintWriter out = response.getWriter();
out.println("<HTML>");
out.println(
"<HEAD><TITLE>セッションカウンタ</TITLE></HEAD>");
out.println("<BODY>");
out.println("<H2>セッションカウンタ</H2>");
HttpSession session=request.getSession();
String NumStr=(String) session.getAttribute("Num");
int Num; //訪問回数
初めて接続するとき
if(NumStr==null) {
out.println("初めまして!ようこそ本ページへ");
Num=1;
}
2 回目以降の接続の場合
else {
Num=(int) Integer.parseInt(NumStr);
Num++;
out.println("あなたは"+Num+"回目の訪問ですね。");
}
out.println("</BODY>");
out.println("</HTML>");
session.setAttribute("Num", String.valueOf(Num));
}
データの値としては整数型や実数型などの基本型は許されない
}
ので、文字列型に変換している。
115