
サーブレット&JSPの基本|ひとこと広場の投稿と閲覧機能を作成する
投稿したひとことを全ユーザーで共有する。GETとPOSTを使い分けて、投稿と閲覧の機能を完成させよう
前回の記事では、ひとこと広場のメイン画面表示とログアウト機能を作成しました。
Main.javaでは、ログイン済みかどうかをセッションスコープのloginUserで確認し、ログインしていればmain.jspへフォワードしました。未ログインの場合は、トップ画面index.jspへリダイレクトしました。
また、メイン画面を表示する前に、アプリケーションスコープへmessageListを用意しました。messageListは、ひとこと投稿を保存するためのリストです。
今回の記事では、このmessageListを使って、ひとことの投稿と閲覧の機能を作成します。
利用者がメイン画面の入力欄に短い文章を入力し、投稿ボタンをクリックすると、その内容がMain.javaへPOSTリクエストとして送信されます。Main.javaでは、ログイン中の利用者名と入力された文章をもとにShortMessageインスタンスを作成します。
作成したShortMessageインスタンスは、アプリケーションスコープに保存されているmessageListの先頭に追加します。
そしてmain.jspでは、アプリケーションスコープからmessageListを取得し、リストの中に入っているひとことを順番に表示します。
これにより、ログインした利用者がひとことを投稿し、ほかの利用者の投稿も閲覧できるようになります。
今回作成する機能
今回作成するのは、ひとこと広場の中心となる投稿と閲覧の機能です。
作成する機能
| 機能 | 内容 |
|---|---|
| ひとこと投稿機能 | 入力欄に書いた短い文章を投稿する |
| ひとこと閲覧機能 | 投稿されたひとことを一覧で表示する |
| 新しい順の表示 | 新しく投稿されたひとことを上に表示する |
| 全ユーザー共有 | 投稿リストをアプリケーションスコープで共有する |
今回の投稿機能では、ログイン中の利用者名と投稿内容を1つのShortMessageインスタンスにまとめます。
投稿されたShortMessageインスタンスは、messageListの先頭に追加します。
そのため、あとから投稿されたひとことほど、画面の上に表示されます。
今回作成・修正するファイル
今回の記事では、次の3つのファイルを作成または修正します。
ファイル一覧
| ファイル | 種類 | 役割 |
|---|---|---|
| PostMessageLogic.java | モデル | ひとことをリストの先頭に追加する |
| Main.java | コントローラ 修正 | POSTで送信されたひとことを受け取り、リストへ追加する |
| main.jsp | ビュー 修正 | 投稿フォームとひとこと一覧を表示する |
ShortMessage.javaとUser.javaは、1本目の記事で作成済みです。
Main.javaとmain.jspは、前回の記事で作成済みです。今回は、投稿と閲覧の機能を追加する形で修正します。
投稿と閲覧の全体像
ひとことの投稿は、メイン画面から始まります。
利用者が入力欄に文章を入力し、投稿ボタンをクリックすると、Main.javaへPOSTリクエストが送られます。
Main.javaは、入力された文章を取得し、セッションスコープからログイン中の利用者情報を取得します。
その後、利用者名と投稿内容を使ってShortMessageインスタンスを作成し、PostMessageLogic.javaに処理を依頼してmessageListの先頭へ追加します。
投稿処理の流れ
| 順番 | 内容 |
|---|---|
| 1 | main.jspでひとことを入力する |
| 2 | 投稿ボタンをクリックする |
| 3 | Main.javaのdoPostメソッドが実行される |
| 4 | リクエストパラメータtextを取得する |
| 5 | セッションスコープからloginUserを取得する |
| 6 | アプリケーションスコープからmessageListを取得する |
| 7 | ShortMessageインスタンスを作成する |
| 8 | PostMessageLogicでmessageListの先頭に追加する |
| 9 | main.jspへフォワードする |
| 10 | 投稿一覧が表示される |
この流れにより、投稿したひとことがメイン画面に表示されます。
図1:ひとこと投稿の流れ

この図から分かること
ひとこと投稿では、main.jspのフォームからMain.javaへPOSTリクエストが送られます。
Main.javaでは、入力された投稿内容をrequest.getParameter("text")で取得します。また、セッションスコープからloginUserを取得し、誰が投稿したのかを確認します。
投稿内容と利用者名をもとにShortMessageインスタンスを作成し、PostMessageLogic.javaでmessageListの先頭へ追加します。
messageListはアプリケーションスコープに保存されているため、すべての利用者で共有できます。
Main.javaはGETとPOSTで役割を分ける
前回作成したMain.javaは、GETリクエストを受け取ってメイン画面を表示するサーブレットでした。
今回の記事では、同じMain.javaにPOSTリクエストを処理するdoPostメソッドを追加します。
つまり、Main.javaはGETとPOSTで異なる役割を持つことになります。
Main.javaの役割
| リクエスト方法 | 実行されるメソッド | 役割 |
|---|---|---|
| GET | doGet | メイン画面を表示する |
| POST | doPost | 投稿されたひとことを登録する |
GETは、メイン画面を表示するときに使います。
POSTは、入力されたひとことを送信して登録するときに使います。
このように、同じサーブレットでも、GETで呼ばれたときとPOSTで呼ばれたときで別の処理を行わせることができます。
投稿内容はリクエストパラメータで受け取る
main.jspの投稿フォームでは、inputタグのname属性にtextを指定します。
そのため、Main.javaでは次のように投稿内容を取得します。
request.getParameter("text")
フォームとサーブレットの対応
| main.jsp側 | Main.java側 |
|---|---|
| inputタグのname属性 text | request.getParameter("text") |
| formタグのaction属性 Main | URLパターン/Mainのサーブレットが実行される |
| formタグのmethod属性 post | doPostメソッドが実行される |
name属性とgetParameterの引数は一致させる必要があります。
ここがずれていると、入力欄に文字を入力しても、サーブレット側では値を取得できません。
空の投稿は追加しない
今回の投稿処理では、入力されたtextが空でない場合だけ、投稿を追加します。
Main.javaでは、次のように確認します。
text != null && text.length() != 0
入力値チェックの考え方
| 状態 | 処理 |
|---|---|
| textに文字が入っている | ShortMessageを作成してmessageListへ追加する |
| textがnull | 投稿を追加しない |
| textの文字数が0 | 投稿を追加しない |
今回の記事では、空の投稿を送信しても、ひとこと一覧には追加しないだけにします。
空投稿時にエラーメッセージを表示する機能は、次の記事で追加します。
ひとことを追加するモデルを作成する
投稿されたひとことをmessageListへ追加する処理は、PostMessageLogic.javaに分けます。
PostMessageLogic.javaは、ShortMessageインスタンスとmessageListを受け取り、messageListの先頭にShortMessageインスタンスを追加します。
ひとことの投稿に関する処理を行うモデルの例
ファイル名: PostMessageLogic.java
package model;
import java.util.List;
public class PostMessageLogic {
public void execute(ShortMessage message,
List<ShortMessage> messageList) {
messageList.add(0, message);
}
}PostMessageLogic.javaは、modelパッケージに作成します。
executeメソッドでは、messageList.add(0, message)を使って、リストの先頭に投稿を追加しています。
PostMessageLogic.javaのポイント
PostMessageLogic.javaは、ひとことをリストへ追加する処理だけを担当します。
画面表示は行いません。
リクエストパラメータの取得も行いません。
PostMessageLogic.javaの役割
| 項目 | 内容 |
|---|---|
| 受け取るもの | ShortMessageインスタンスとList |
| 行う処理 | messageListの先頭にShortMessageを追加する |
| 使うメソッド | messageList.add(0, message) |
| 戻り値 | なし |
messageList.add(0, message)では、第1引数に追加位置、第2引数に追加するインスタンスを指定しています。
0はリストの先頭を表します。
そのため、新しい投稿がリストの先頭に入り、画面では新しい投稿が上に表示されます。
ArrayListのaddで先頭に追加する
ArrayListのaddには、要素を末尾に追加する使い方と、指定した位置に挿入する使い方があります。
今回使っているのは、指定した位置に挿入する使い方です。
messageList.add(0, message)
addの指定
| 指定 | 内容 |
|---|---|
| 第1引数 0 | 追加する位置。0は先頭を表す |
| 第2引数 message | 追加するShortMessageインスタンス |
すでにリストに投稿が入っている場合、先頭に新しい投稿を追加すると、既存の投稿は1つずつ後ろにずれます。
上書きされるわけではありません。
先頭に追加されるイメージ
| 操作前の順番 | 投稿 |
|---|---|
| 0番目 | 山田:今日もServletの復習をしました |
| 1番目 | 青山:JSPの画面表示が分かってきました |
新しい投稿として、佐藤:エラーが解決しましたをmessageList.add(0, message)で追加すると、次のようになります。
| 操作後の順番 | 投稿 |
|---|---|
| 0番目 | 佐藤:エラーが解決しました |
| 1番目 | 山田:今日もServletの復習をしました |
| 2番目 | 青山:JSPの画面表示が分かってきました |
この仕組みにより、新しいひとことを上に表示できます。
図2:messageListの先頭に新しい投稿を追加する

この図から分かること
messageList.add(0, message)を使うと、新しいShortMessageインスタンスをリストの先頭に追加できます。
すでにリストに入っている投稿は上書きされず、1つずつ後ろにずれます。
そのため、投稿一覧を先頭から順に表示すれば、新しい投稿ほど上に表示されます。
ひとこと広場では、この仕組みを使って、新着順の投稿一覧を作ります。
Main.javaにdoPostを追加する
次に、Main.javaへ投稿処理を追加します。
前回作成したdoGetメソッドはそのまま使います。
今回追加するのは、POSTリクエストを処理するdoPostメソッドです。
doPostで行う処理
| 順番 | 処理 | 内容 |
|---|---|---|
| 1 | 文字コードを指定する | request.setCharacterEncoding("UTF-8")を実行する |
| 2 | textを取得する | 投稿内容をリクエストパラメータから取得する |
| 3 | 空でないか確認する | 入力されている場合だけ投稿処理を行う |
| 4 | messageListを取得する | アプリケーションスコープから投稿リストを取得する |
| 5 | loginUserを取得する | セッションスコープからログイン中の利用者を取得する |
| 6 | ShortMessageを作成する | 利用者名と投稿内容をまとめる |
| 7 | PostMessageLogicを実行する | messageListの先頭へ投稿を追加する |
| 8 | messageListを保存する | アプリケーションスコープへ保存する |
| 9 | main.jspへフォワードする | 更新後のメイン画面を表示する |
doPostでは、投稿内容が空でない場合だけmessageListへ追加します。
空の場合は、今回の記事では何も追加せず、そのままmain.jspへフォワードします。
ひとことに関するリクエストを処理するコントローラを修正する
Main.javaにdoPostメソッドを追加します。
前回作成したdoGetメソッドは変更しないため、ここでは省略して、追加するdoPostメソッドを中心に示します。
ひとことに関するリクエストを処理するコントローラを修正した例
ファイル名: Main.java
package servlet;
import java.io.IOException;
import java.util.List;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import model.PostMessageLogic;
import model.ShortMessage;
import model.User;
@WebServlet("/Main")
public class Main extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// ひとことリストをアプリケーションスコープから取得
ServletContext application = this.getServletContext();
List<ShortMessage> messageList =
(List<ShortMessage>) application.getAttribute("messageList");
// 取得できなかった場合は、ひとことリストを新規作成して
// アプリケーションスコープに保存
if (messageList == null) {
messageList = new ArrayList<>();
application.setAttribute("messageList", messageList);
}
// ログインしているか確認するため、
// セッションスコープからユーザー情報を取得
HttpSession session = request.getSession();
User loginUser =
(User) session.getAttribute("loginUser");
if (loginUser == null) {
// ログインしていない場合はトップ画面へリダイレクト
response.sendRedirect("index.jsp");
return;
} else {
// ログイン済みの場合はメイン画面へフォワード
RequestDispatcher dispatcher =
request.getRequestDispatcher(
"WEB-INF/jsp/main.jsp");
dispatcher.forward(request, response);
}
}
@SuppressWarnings("unchecked")
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// リクエストパラメータの取得
request.setCharacterEncoding("UTF-8");
String text = request.getParameter("text");
// 入力値チェック
if (text != null && text.length() != 0) {
// アプリケーションスコープに保存されたひとことリストを取得
ServletContext application = this.getServletContext();
List<ShortMessage> messageList =
(List<ShortMessage>)
application.getAttribute("messageList");
// セッションスコープに保存されたユーザー情報を取得
HttpSession session = request.getSession();
User loginUser =
(User) session.getAttribute("loginUser");
// ひとことを作成してひとことリストに追加
ShortMessage message =
new ShortMessage(loginUser.getName(), text);
PostMessageLogic postMessageLogic =
new PostMessageLogic();
postMessageLogic.execute(message, messageList);
// アプリケーションスコープにひとことリストを保存
application.setAttribute("messageList", messageList);
}
// メイン画面にフォワード
RequestDispatcher dispatcher =
request.getRequestDispatcher("WEB-INF/jsp/main.jsp");
dispatcher.forward(request, response);
}
}Main.javaは、servletパッケージに作成済みのファイルを修正します。
この例では、前回作成したdoGetメソッドは省略しています。実際のファイルでは、前回のdoGetメソッドを残したまま、doPostメソッドを追加します。
Main.javaのdoPostのポイント
doPostでは、投稿処理に必要な情報を3つ扱っています。
doPostで扱う情報
| 情報 | 取得元 | 用途 |
|---|---|---|
| text | リクエストパラメータ | 投稿内容として使う |
| loginUser | セッションスコープ | 誰が投稿したかを判断する |
| messageList | アプリケーションスコープ | 投稿を追加するリストとして使う |
textは、main.jspの入力欄から送信されます。
loginUserは、ログイン成功時にセッションスコープへ保存されたUserインスタンスです。
messageListは、Main.javaのdoGetで用意したアプリケーションスコープ内の投稿リストです。
この3つを組み合わせることで、誰が何を投稿したのかをShortMessageインスタンスとして作成できます。
アプリケーションスコープのmessageListを更新する
投稿されたひとことは、アプリケーションスコープのmessageListに追加します。
messageListは、全ユーザーで共有する投稿一覧です。
そのため、山田さんが投稿したあとに青山さんがログインしても、山田さんの投稿を閲覧できます。
messageListをアプリケーションスコープに保存する理由
| 理由 | 内容 |
|---|---|
| 全ユーザーで共有するため | ほかの利用者の投稿も表示できる |
| リクエストをまたいで保存するため | 投稿後に再表示しても一覧に残る |
| メイン画面で共通利用するため | main.jspで一覧表示に使う |
今回のMain.javaでは、投稿を追加したあとにapplication.setAttribute("messageList", messageList)を実行しています。
getAttributeで取得したリストに追加した時点で、アプリケーションスコープ内のリストにも変更が反映されます。ただし、学習段階では、更新後にsetAttributeで保存し直す流れとして覚えると、処理の流れが分かりやすくなります。
メイン画面に投稿フォームと一覧を追加する
次に、main.jspを修正します。
前回のmain.jspでは、ログイン中の利用者名とログアウトリンクだけを表示していました。
今回は、次の内容を追加します。
main.jspに追加する内容
| 追加する内容 | 役割 |
|---|---|
| 更新リンク | MainへGETリクエストを送り、画面を再表示する |
| 投稿フォーム | ひとことを入力してMainへPOST送信する |
| 投稿一覧 | messageListに入っているひとことを表示する |
投稿フォームでは、formタグのaction属性にMain、method属性にpostを指定します。
これにより、投稿ボタンをクリックしたときに、Main.javaのdoPostメソッドが実行されます。
メイン画面を出力するビューを修正する
main.jspを次のように修正します。
メイン画面を出力するビューを修正した例
ファイル名: main.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<%@ page import="model.User, model.ShortMessage, java.util.List" %>
<%
// セッションスコープに保存されたユーザー情報を取得
User loginUser =
(User) session.getAttribute("loginUser");
// アプリケーションスコープに保存されたひとことリストを取得
List<ShortMessage> messageList =
(List<ShortMessage>)
application.getAttribute("messageList");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ひとこと広場</title>
</head>
<body>
<h1>ひとこと広場メイン</h1>
<p>
<%= loginUser.getName() %>さん、ログイン中
<a href="Logout">ログアウト</a>
</p>
<p><a href="Main">更新</a></p>
<form action="Main" method="post">
<input type="text" name="text">
<input type="submit" value="投稿">
</form>
<% for (ShortMessage message : messageList) { %>
<p><%= message.getUserName() %>:<%= message.getText() %></p>
<% } %>
</body>
</html>main.jspは、src/main/webapp/WEB-INF/jspディレクトリにあるファイルを修正します。
このJSPでは、セッションスコープからloginUserを取得し、アプリケーションスコープからmessageListを取得しています。
その後、投稿フォームと投稿一覧を表示しています。
main.jspのポイント
main.jspでは、User、ShortMessage、Listを使うため、pageディレクティブでインポートしています。
インポートするクラス
| クラス | 使う理由 |
|---|---|
| model.User | ログイン中の利用者情報を扱うため |
| model.ShortMessage | ひとこと投稿を扱うため |
| java.util.List | 投稿リストを扱うため |
messageListは、アプリケーションスコープから取得します。
JSPでは、アプリケーションスコープを操作するために、暗黙オブジェクトapplicationを使えます。
application.getAttribute("messageList")で取得した戻り値はObject型なので、Listへキャストしています。
投稿フォームのしくみ
main.jspの投稿フォームは、次のようになっています。
フォームの指定
| 指定 | 内容 |
|---|---|
| action属性 Main | Main.javaへリクエストを送る |
| method属性 post | POSTで送信する |
| inputタグのname属性 text | 投稿内容をtextという名前で送る |
| submitボタン | クリックするとフォームを送信する |
投稿ボタンをクリックすると、Main.javaのdoPostメソッドが実行されます。
doPostでは、request.getParameter("text")で入力されたひとことを取得します。
拡張for文で投稿一覧を表示する
main.jspでは、messageListに入っているShortMessageインスタンスを、拡張for文で順番に取り出しています。
<% for (ShortMessage message : messageList) { %>
この文では、messageListに入っているShortMessageインスタンスが、1つずつmessage変数に代入されます。
拡張for文の考え方
| 部分 | 内容 |
|---|---|
| ShortMessage | リストから取り出すインスタンスの型 |
| message | 取り出したインスタンスを入れる変数 |
| messageList | 繰り返しの対象になるリスト |
ループの中では、message.getUserNameで投稿者名を取得し、message.getTextで投稿内容を取得しています。
これにより、投稿者名:投稿内容という形で画面に表示できます。
図3:main.jspでひとこと一覧を表示する流れ

この図から分かること
main.jspでは、アプリケーションスコープからmessageListを取得します。
messageListには、投稿されたShortMessageインスタンスが保存されています。
拡張for文を使ってmessageListの先頭から順に取り出すことで、ひとこと一覧を画面に表示できます。
PostMessageLogic.javaで新しい投稿を0番目に追加しているため、画面にも新しい投稿から順に表示されます。
更新リンクの役割
main.jspには、次の更新リンクを追加しています。
更新
このリンクをクリックすると、Main.javaへGETリクエストが送られます。
GETリクエストなので、Main.javaのdoGetメソッドが実行され、現在のmessageListを使ってメイン画面が再表示されます。
更新リンクの動き
| 操作 | 実行される処理 |
|---|---|
| 更新リンクをクリックする | MainへGETリクエストを送る |
| Main.java | doGetメソッドを実行する |
| main.jsp | 現在の投稿一覧を表示する |
この更新リンクは、投稿一覧を再表示したいときに使います。
別の利用者が投稿した内容を確認したい場合にも、更新リンクをクリックすることで最新のmessageListを表示できます。
投稿と閲覧機能の動作確認
ここまで作成できたら、投稿と閲覧の機能を確認します。
まずは、1人の利用者でログインし、複数のひとことを投稿してみましょう。
1人の利用者で確認する手順
| 手順 | 内容 |
|---|---|
| 1 | ブラウザでhttp://localhost:8080/Hiroba/へアクセスする |
| 2 | 任意の利用者名とパスワード1234でログインする |
| 3 | ログイン結果画面からひとこと投稿・閲覧へをクリックする |
| 4 | メイン画面の入力欄に1つ目のひとことを入力する |
| 5 | 投稿ボタンをクリックする |
| 6 | 投稿したひとことが表示されることを確認する |
| 7 | 2つ目のひとことを投稿する |
| 8 | 2つ目のひとことが1つ目の上に表示されることを確認する |
| 9 | 空のまま投稿する |
| 10 | 空の投稿が一覧に追加されないことを確認する |
新しい投稿が上に表示されれば、messageList.add(0, message)が正しく動作しています。
空の投稿が追加されなければ、text.length() != 0のチェックが働いています。
ブラウザの表示例

複数ユーザーで共有されることを確認する
次に、アプリケーションスコープに保存したmessageListが、複数の利用者で共有されることを確認します。
複数ユーザーで確認する手順
| 手順 | 内容 |
|---|---|
| 1 | ユーザーAでログインする |
| 2 | ユーザーAとしてひとことを投稿する |
| 3 | ユーザーAをログアウトする |
| 4 | ブラウザを閉じる |
| 5 | ユーザーBでログインする |
| 6 | ユーザーAの投稿が表示されることを確認する |
| 7 | ユーザーBとしてひとことを投稿する |
| 8 | ユーザーBの投稿がユーザーAの投稿より上に表示されることを確認する |
| 9 | ユーザーBをログアウトする |
| 10 | 再度ユーザーAでログインする |
| 11 | ユーザーAとユーザーBの投稿が表示されることを確認する |
messageListはアプリケーションスコープに保存されているため、ユーザーごとのセッションスコープとは違い、全ユーザーで共有されます。
そのため、ユーザーAが投稿した内容を、ユーザーBも閲覧できます。
ブラウザの表示例:山田の投稿画面

ブラウザの表示例:青山の当行画面

アプリケーションスコープに保存した投稿の有効期間
messageListはアプリケーションスコープに保存しています。
そのため、Webアプリケーションが動いている間は、投稿されたひとことが残ります。
ただし、サーバを停止したり再起動したりすると、アプリケーションスコープに保存されていたmessageListは消滅します。
投稿が残る場合と消える場合
| 操作 | 投稿一覧 |
|---|---|
| 同じブラウザで更新する | 残る |
| ブラウザを閉じて再度ログインする | Webアプリケーションが動いていれば残る |
| 別の利用者でログインする | Webアプリケーションが動いていれば表示される |
| サーバを停止する | 消える |
| サーバを再起動する | 消える |
| Webアプリケーションが再読み込みされる | 消えることがある |
今回は、データベースやファイル保存を使っていません。
そのため、投稿内容を長期的に保存することはできません。アプリケーションスコープは、Webアプリケーションが動いている間だけ利用できる共有領域です。
今回作成したファイル
今回の記事では、ひとこと広場の投稿と閲覧機能を作成しました。
作成・修正したファイル
| ファイル | 保存場所 | 内容 |
|---|---|---|
| PostMessageLogic.java | src/main/java/model | ひとことをmessageListの先頭に追加する |
| Main.java | src/main/java/servlet | doPostを追加し、投稿処理を行う |
| main.jsp | src/main/webapp/WEB-INF/jsp | 投稿フォームと投稿一覧を表示する |
今回のポイントは、Main.javaがGETとPOSTで異なる動きをすることです。
GETで呼ばれたときはメイン画面を表示し、POSTで呼ばれたときは投稿処理を行います。
また、投稿一覧はアプリケーションスコープのmessageListに保存します。これにより、ひとこと広場を利用するすべてのユーザーで投稿を共有できます。
次の記事では、空の投稿を送信したときに、エラーメッセージを表示する機能を追加します。エラーメッセージは、フォワード先のmain.jspで一度だけ使う情報なので、リクエストスコープに保存して表示します。
