Servlet&JSPでFormデータ

今回はFormデータのやり取りをJavaServeletとJSPで交互に行う練習をVsCodeで行いましょう

JSP->ServletのFormデータ

では、まず 自由にアクセスできるindex.jspを作って、そこで入力されたデータをservletに渡すまでの実験をしてみましょう。

JSPからServletを呼び出す

前回行ったようにVsCodeでmavenを作っていきます。一度、CTLR+SHIFT+Pでcreate maven projectがしてあれば、左下のMAVENの表示のところにある+アイコンをクリックして、先ほどと同じようにVsJToSというプロジェクトを作れます。やってなければcreate maven projectしてください。

WEB-INFにあるindex.jspを最初に起動して、そこで入力したdataをServLetで受けるものを作ります

JSPにformを作る

最初に作られたファイルの中身は次の様なものです。

	<html>
	<body>
	<h2>Hello World!</h2>
	</body>
	</html>

これにformを入れていきます。まじめに全部入れてもいいのですが、せっかくVsCodeで zencodingが使えるのですから、一旦全ソースを消して、先頭で !またはhtml:5と入力してTABキーを押してください。html5のひな形が作成されます。そしてコードを修正します。

src/main/webapp/index.jsp
	<%@ page language="java" contentType="text/html; charset=UTF-8"
		pageEncoding="UTF-8"%>
	<!DOCTYPE html>
	<html lang="ja">
	<head>
		<meta charset="UTF-8">
		<meta http-equiv="X-UA-Compatible" content="IE=edge">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>JSPからServlet呼び出し</title>
	</head>
	<body>
	<h1>JSPからServletにデータを渡す実験</h1>	
	<form action="GetData" method="POST">
		data:<input type="text" name="abc">
		<input type="submit" value="送信">
	</form>
	</body>
	</html>

これでdata:の後ろにテキストボックスができて、そこに入れたdataがabcという名前が付いてServLetに渡っていきます。

渡されたデータを受け取って出力する

Javaのソースは以前やったようにドメイン名の逆の階層を作ってその中に作成します

	+---VsJToS
		\---vsjtos
			|   pom.xml
			|
			+---.vscode
			|       settings.json
			|
			+---src
				\---main
					+---java
					|   \---jp
					|       \---co
					|           \---etlab
					|                   GetData.java
					|
					\---webapp
						|   index.jsp
						|
						\---WEB-INF
								web.xml

階層の下に作るのでpackage名もそれに対応させます。そして以下のように入力してください。出力は簡単のため画面ではなく、ログとして出力します。

src/main/java/jp/co/etlab/GetData.java
	package jp.co.etlab;

	import java.io.IOException;
	
	import javax.servlet.ServletException;
	import javax.servlet.annotation.WebServlet;
	import javax.servlet.http.HttpServlet;
	import javax.servlet.http.HttpServletRequest;
	import javax.servlet.http.HttpServletResponse;
	
	@WebServlet("/GetData")
	public class GetData extends HttpServlet {
		private static final long serialVersionUID = 1L;
	
		protected void doPost(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
			request.setCharacterEncoding("UTF-8");
			String fdata = request.getParameter("abc");
	
			System.out.println("form-data:" + fdata);
		}
	}

以前やったように、javaxがエラーになりますので、pom.xmlにjavax.servlet-apiを追加して保存して、nowで設定してください

MAVENでコンパイルしてTOMCATで実行

以前やったように、MAVENの自分のプロジェクトを右クリックして, install,compile,packageを行います。

warファイルができたら、右クリックして Run on Tomcat Serverして、tomcatを展開してそのプロジェクトをOpen in Browserで実行します。

open in browser

入力フォームが表示されたら適当な文字を入力して、送信をクリックします。

index.jsp

送信してたら画面は真っ白になりますが、出力コンソールに受け取ったデータが出力されています。

log

今回のサンプルは自由にアクセスができる webappのindex.jspに直接アクセスして、入力されたデータをPOSTで送り、ServLetでdoPOSTで受信して、表示するものでした。

Servlet->JSPのFormデータ

次は逆に先にServletをGETで呼び出して、なにか値をJSPに渡すようにしましょう。

ServletからJSPを呼び出す

今度は先にServletを作りましょう。mainの下にjava\jp\co\etlbaと作り、SJServlet.javaを作成します。

src/main/java/jp/co/etlab/SJServlet.java
	package jp.co.etlab;

	import java.io.IOException;
	
	import javax.servlet.ServletException;
	import javax.servlet.annotation.WebServlet;
	import javax.servlet.http.HttpServlet;
	import javax.servlet.http.HttpServletRequest;
	import javax.servlet.http.HttpServletResponse;
	
	@WebServlet("/SJServlet")
	public class SJServlet extends HttpServlet {
		private static final long serialVersionUID = 1L;
	
		protected void doGet(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
			String sdata = request.getRemoteAddr();
			response.sendRedirect("index.jsp?sdata=" + sdata);
		}
	}

今回もjavaxでエラーになるので、pom.xmlのdependenciesにjavaxのlibを追加

vsstoj/pom.xml
	<dependencies>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
		</dependency>

先にSJServletが起動されます。この時ServletにはGETでやってきますのでdoGETで受けます。そこからJSPをパラメータ付きでリダイレクトするものです。

リダイレクトはwebappにあるJSPに使えます。今回はIPアドレスをurlに付けてGETで渡しています。

つづいて、JSP側を記述します。index.jspを修正します。

src/main/webapp/index.jsp
	<%@ page language="java" contentType="text/html; charset=UTF-8"
		pageEncoding="UTF-8"%>
	<%
	String sd = request.getParameter("sdata");
	%>
	<!DOCTYPE html>
	<html>
	<head>
		<meta charset="UTF-8">
		<title>ログイン</title>
		<meta name="viewport" content="width=device-width, initial-scale=1">
	</head>
	<body>
	<h2>Servletが送ったデータ</h2>
	<p><%= sd %></p>
	</body>
	</html>

DOCTYPEの前にServletから送られてきたGETデータを受信しておきます。そしてJSPの中で表示します。

MAVENでコンパイルしてTOMCATで実行

以前やったように、MAVENの自分のプロジェクトを右クリックして, install,compile,packageを行います。

warファイルができたら、右クリックして Run on Tomcat Serverして、tomcatを展開してそのプロジェクトをOpen in Browserで実行します。

あとは前回と同じです。実行すると

VSSToJ

最初の起動はindex.jspをいきなり起動したものになっているので、アドレスはNullで表示されます。これではServletから呼び出していません。そこでurlの後ろに /SJServlet を付けてServletを呼び出します。

/VSSToJ

今度は一度Servletに渡ってから、またindex.jspに返ってきているのでアドレスが渡ってきています。つまりServletからJSPを呼び出していることになります。

Servelet->WEB-INFのJSPのFormデータ

次は、ServletからWEB-INF内にあるリダイレクトできない場所のJspをフォーワードしてみましょう。その中でセッションデータを使ってみましょう。

図にするとこのようになります。jspとservletがバトンパスしてますね。

概要図

ServletからWEB-INF内のJspをforwardする

先にJspファイルを作っていきます。最初に作るindex.jspは外部に開放された場所なので、誰でも来ることができます。なのでログイン前の画面などに使います。

Mavenを使ってひな形を作りましょう。以前のものと同じ流れです。今回はVsForwardというプロジェクトで作成します。

src/main/webapp/index.jsp
	<%@ page language="java" contentType="text/html; charset=UTF-8"
		pageEncoding="UTF-8"%>
	<!DOCTYPE html>
	<html lang="ja">
	<head>
		<meta charset="UTF-8">
		<meta http-equiv="X-UA-Compatible" content="IE=edge">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>JSPからServlet呼び出し</title>
	</head>
	<body>
	<h1>JSPからServletにデータを渡す実験</h1>	
	<form action="Login" method="POST">
		data:<input type="text" name="abc">
		<input type="submit" value="送信">
	</form>
	</body>
	</html>

つづいて受信側のjavaをsrc\main\java\jp\co\etlabにLogin.javaとして作ります。

src/main/java/jp/co/etlab/Login.java
	package jp.co.etlab;

	import java.io.IOException;
	
	import javax.servlet.RequestDispatcher;
	import javax.servlet.ServletException;
	import javax.servlet.annotation.WebServlet;
	import javax.servlet.http.HttpServlet;
	import javax.servlet.http.HttpServletRequest;
	import javax.servlet.http.HttpServletResponse;
	import javax.servlet.http.HttpSession;
	
	@WebServlet("/Login")
	public class Login extends HttpServlet {
		private static final long serialVersionUID = 1L;
	
		protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
			request.setCharacterEncoding("UTF-8");
			String name = request.getParameter("name");
			String pass = request.getParameter("pass");
			
			if (pass.equals("12345")) {
				//ユーザー情報ををセッションスコープに保存
				HttpSession session = request.getSession();
				session.setAttribute("loginUser", name);
			}
			//mainをフォワード
			RequestDispatcher dispatcher =
					request.getRequestDispatcher("/WEB-INF/jsp/main.jsp");
			dispatcher.forward(request, response);
		}
	}

このソースはindex.jspから送られてきたnameとpassを受け取り、passが12345の時はsessionスコープにnameを保存して、main.jspにフォワードします。passがなくてもforwardしますが、その時はsessionがないのでログイン失敗と表示してLoginに戻るようにします。

では、そのmain.jspを作っていきましょう。WEB-INFの下にjspフォルダーを作り、その中にmain.jspを作ります。

ソースの階層を出しておきます

	+---VsForward
	|   \---vsforward
	|       |   pom.xml
	|       |
	|       +---.vscode
	|       |       settings.json
	|       |
	|       +---src
	|       |   \---main
	|       |       +---java
	|       |       |   \---jp
	|       |       |       \---co
	|       |       |           \---etlab
	|       |       |                   Login.java
	|       |       |                   Logout.java
	|       |       |                   Main.java
	|       |       |
	|       |       \---webapp
	|       |           |   index.jsp
	|       |           |
	|       |           \---WEB-INF
	|       |               |   web.xml
	|       |               |
	|       |               \---jsp
	|       |                       logout.jsp
	|       |                       main.jsp
	|       |                       secret.jsp

以下のソースも階層を間違えないように入力してください

src/main/webapp/WEB-INF/jsp/main.jsp
	<%@ page language="java" contentType="text/html; charset=UTF-8"
		pageEncoding="UTF-8"%>
	<%
	String loginUser = (String)session.getAttribute("loginUser");
	%>
	<!DOCTYPE html>
	<html>
	<head>
		<meta charset="UTF-8">
		<title>ログイン</title>
		<meta name="viewport" content="width=device-width, initial-scale=1">
	</head>
	<body>
		<h1>ログイン</h1>
		<% if(loginUser != null) { %>
			<p>ログイン成功しました</p>
			<p>ようこそ<%= loginUser %>さん</p>
			<a href="/vsforward/Main">秘密の部屋へ</a>
		<% } else { %>
			<p class="text-danger">ログインに失敗しました</p>
			<a href="/vsforward/">loginへ</a>
		<% } %>
	</body>
	</html>

DOCTYPEを実行する前に、Login.javaでsessionスコープに保存したユーザー名を取得しています。取得できていればログイン成功ですが、だめなら失敗です。

Main.javaは本来ならばデータベースなどを読み込む場所ですが、今回は特になにもしていません。sessionスコープをチェックして次につないでいるだけです。

src/main/java/jp/co/etlab/Main.java
	package jp.co.etlab;

	import java.io.IOException;
	
	import javax.servlet.RequestDispatcher;
	import javax.servlet.ServletContext;
	import javax.servlet.ServletException;
	import javax.servlet.annotation.WebServlet;
	import javax.servlet.http.HttpServlet;
	import javax.servlet.http.HttpServletRequest;
	import javax.servlet.http.HttpServletResponse;
	import javax.servlet.http.HttpSession;
	
	@WebServlet("/Main")
	public class Main extends HttpServlet {
		private static final long serialVersionUID = 1L;
	
		protected void doGet(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
			//ログインしているか
			HttpSession session = request.getSession();
			String loginUser = (String) session.getAttribute("loginUser");
			
			if (loginUser == null) {
				response.sendRedirect("/vsforward/");
			} else {
				RequestDispatcher dispatcher =
						request.getRequestDispatcher("/WEB-INF/jsp/secret.jsp");
				dispatcher.forward(request, response);
			}
		}
	}

そして、secret.jspは本来なら読み込んだ物などを表示するところですが、なにも秘密はありませんので、sessionスコープを消してログアウトしておしまいです。

src/main/webapp/WEB-INF/jsp/secret.jsp
		<%@ page language="java" contentType="text/html; charset=UTF-8"
		pageEncoding="UTF-8"%>
	<%
	String loginUser = (String)session.getAttribute("loginUser");
	%>
	<!DOCTYPE html>
	<html>
	<head>
		<meta charset="UTF-8">
		<title>秘密の部屋</title>
		<meta name="viewport" content="width=device-width, initial-scale=1">
	</head>
	<body>
		<h1>秘密の部屋</h1>
		<% if(loginUser != null) { %>
			<p>特に秘密はありません</p>
			<p>では<%= loginUser %>さん、さようなら</p>
			<a href="/vsforward/Logout">ログアウトへ</a>
		<% } else { %>
			<p class="text-danger">ログインできていません</p>
			<a href="/vsforward/">loginへ</a>
		<% } %>
	</body>
	</html>

あとはログアウトするだけになります。Servletでsessionを消してログアウト終了画面を出します。

src/main/java/jp/co/etlab/Logout.java
	package jp.co.etlab;

	import java.io.IOException;
	
	import javax.servlet.RequestDispatcher;
	import javax.servlet.ServletException;
	import javax.servlet.annotation.WebServlet;
	import javax.servlet.http.HttpServlet;
	import javax.servlet.http.HttpServletRequest;
	import javax.servlet.http.HttpServletResponse;
	import javax.servlet.http.HttpSession;
	
	@WebServlet("/Logout")
	public class Logout extends HttpServlet {
		private static final long serialVersionUID = 1L;
	
		protected void doGet(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
			//セッションスコープを破棄
			HttpSession session = request.getSession();
			session.invalidate();
			
			RequestDispatcher dispatcher =
					request.getRequestDispatcher("/WEB-INF/jsp/logout.jsp");
			dispatcher.forward(request, response);
		}
	}

上のLogout.javaでセッションを削除してからlogout.jspに渡します。

src/main/webapp/WEB-INF/jsp/logout.jsp
	<%@ page language="java" contentType="text/html; charset=UTF-8"
		pageEncoding="UTF-8"%>
	<!DOCTYPE html>
	<html>
	<head>
		<meta charset="UTF-8">
		<title>ログアウト</title>
		<meta name="viewport" content="width=device-width, initial-scale=1">
	</head>
	<body>
		<h1>ログアウト</h1>
		<>ログアウトしました</p>
		<a href="/vsforward/">loginへ</a>
	</body>
	</html>

WEB-INFにあるjspはリダイレクトできません。Servletからフォワードする必要があります。なのでServletからgetDispatcherでフォワードしています。またMain.javaはリクエストされても、セッションがなければLoginに押し戻されるようにコーディングしてあるので、Loginなしでは入れません。

Logoutでセッションを切らないとセッションが残ります。ただしブラウザを閉じてしまえば消えます。