3.フォームとセッション
学習目標
- フォームを構成する
<form>・<label>・<input>・送信ボタンの役割を説明できる - フォームから送信された値を
POSTで受け取れる - HTTP がリクエストごとに独立していることを説明できる
- セッションで、複数のリクエストにまたがる値を保持できる
1. フォーム
Section titled “1. フォーム”ここまでのサーブレットは、サーバーが用意した値を表示するだけでした。利用者が入力した値を受け取るには、入力欄と送信ボタンを持つ画面が必要です。入力欄と送信ボタンからなる HTML を フォーム と呼びます。
名前を入力するフォーム form.html です。
<!DOCTYPE html><html><head> <title>名前の入力</title></head><body> <form action="/greet" method="post"> <label for="name">名前:</label> <input type="text" id="name" name="name"> <button type="submit">送信</button> </form></body></html>フォームは <form> の中に、ラベルの <label>、入力欄の <input>、送信ボタンの <button> を並べて作ります。それぞれの役割は次のとおりです。
| タグ | 役割 |
|---|---|
<form> | 入力欄と送信ボタンをまとめ、送信先と送信方法を指定する |
<label> | 入力欄が何を入力する欄かを示す見出し |
<input> | 利用者が値を入力する欄 |
<button type="submit"> | 押すとフォームの内容を送信するボタン |
<form> には 2 つの属性を指定しています。action は入力値の送信先のパス、method は送信に使う HTTP メソッドです。この例では、送信先が /greet、メソッドが post です。
<input> の type="text" は 1 行のテキスト入力欄を表します。name="name" の name 属性は、入力された値に付けるパラメーター名です。送信時に「どの欄の値か」を識別するため、<input> には name を付けます。
<label> は、その入力欄が何を入力する欄かを画面に表示します。<label> の for と <input> の id を同じ値にすると、2 つが結びつきます。この例ではどちらも name で、ラベルの「名前:」をクリックすると入力欄にカーソルが移ります。id は同じページの中で要素を識別する属性で、name(送信される値のパラメーター名)とは役割が異なります。
フォームは値を埋め込まない静的な HTML なので、src/main/resources/static に .html ファイルとして置きます。src/main/resources/static は、Spring Boot が静的ファイルを配信する場所です。ここに置いたファイルはファイル名がそのままパスになり、form.html なら /form.html でブラウザーから開けます。
1-1. フォームの送信
Section titled “1-1. フォームの送信”type="submit" のボタンを押すと、ブラウザーは次の処理を行います。まずフォーム内の各 <input> の値を name=値 の形に集めます。次にそれを <form> の action のパスへ、method の HTTP メソッドで送信します。最後に、返ってきたレスポンスを新しいページとして表示します。
form.html で 田中 と入力して送信すると、name=田中 が /greet へ POST で送られ、サーバーが返した HTML に画面が切り替わります。
リクエストのメソッドには GET と POST があり、HTTP とサーブレット では GET を扱いました。2 つは値の送信先が異なります。
| メソッド | 用途 | 値の送信先 |
|---|---|---|
GET | リソースの取得 | URL のクエリパラメーター(/greet?name=田中) |
POST | データの送信 | リクエストボディ |
GET は値が URL に現れます。利用者の入力のように URL に残したくない値には適しません。フォームの送信には POST を使い、値は URL に現れないリクエストボディに入れて送ります。form.html の method="post" は、この POST を指定しています。
1-2. 値の受け取り
Section titled “1-2. 値の受け取り”GET を doGet で処理したように、POST は doPost で処理します。送られた値は、GET のクエリパラメーターと同じく request.getParameter で取得します。引数には、<input> の name を渡します。
/greet への POST を受け取り、入力された名前であいさつを返す GreetServlet です。
package com.example.hello;
import jakarta.servlet.annotation.WebServlet;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;
@WebServlet("/greet")public class GreetServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { // 入力欄 name の値を取得する String name = request.getParameter("name");
response.setContentType("text/html; charset=UTF-8"); PrintWriter out = response.getWriter(); out.println("<p>こんにちは、" + name + "さん</p>"); }}request.getParameter("name") が、<input name="name"> に入力された値を返します。田中 と入力して送信すると、name は 田中 になり、次の HTML が表示されます。
<p>こんにちは、田中さん</p>/form.html を開いて名前を入力し、送信ボタンを押すと、GreetServlet の doPost が呼ばれ、入力した名前を含むあいさつが表示されます。
ここでは表示を out.println で組み立てています。JSP で扱ったように、本来は表示を JSP に分けますが、この章はフォームの受け取りとセッションに集中するため、表示は最小限の out.println にとどめます。
リクエストのメソッドに応じて doGet と doPost が呼び分けられるのは、多態性 の働きです。同じパスでも、GET でアクセスすれば doGet、フォームを POST で送信すれば doPost が呼ばれます。
1-3. リクエストの独立性
Section titled “1-3. リクエストの独立性”HTTP では、個々のリクエストは互いに独立しています。サーバーは 1 つのリクエストにレスポンスを返すと、そのやり取りで使った値を保持しません。次のリクエストは、前のリクエストの内容を知らない状態で届きます。
そのため、GreetServlet が受け取った名前も、あいさつを表示した時点で失われます。続けて別のページを開いても、サーバーに先ほどの名前は残っていません。
| リクエスト | サーバーが保持する値 |
|---|---|
/greet(1 回目) | 入力された 田中 |
| 別のページ(2 回目) | 1 回目の名前は残っていない |
利用者の名前のように、複数のページにまたがって使いたい値は、リクエストの独立を越えて保持する仕組みが要ります。それがセッションです。
2. セッション
Section titled “2. セッション”セッション は、利用者ごとにサーバー側で値を保持する仕組みです。1 つのリクエストが終わっても値が残り、同じ利用者からの次のリクエストで取り出せます。リクエストの独立を越えて、利用者という単位で値を持ち越せます。
2-1. セッションの仕組み
Section titled “2-1. セッションの仕組み”複数のブラウザーが同時にアクセスしても、サーバーは利用者ごとに別々の値を保持します。誰の値かを区別するため、サーバーは利用者ごとに保管領域を用意し、それぞれに セッション ID という識別子を割り当てます。
セッション ID は、最初のリクエストへのレスポンスでブラウザーに渡されます。ブラウザーはこの ID を保存し、次からのリクエストで毎回サーバーに送り返します。サーバーは送られてきた ID を見て、どの利用者の保管領域かを判別します。
ブラウザーが ID を送り返すのは、Cookie の働きです。Cookie は、サーバーがブラウザーに保存させる小さな値で、ブラウザーは同じサーバーへのリクエストのたびに自動で送り返します。セッション ID はこの Cookie に入れて渡されるため、利用者が ID を意識することはありません。
2-2. セッションへの保存
Section titled “2-2. セッションへの保存”セッションは request.getSession() で取得します。戻り値の HttpSession が、その利用者の保管領域です。値の保存には、request と同じく setAttribute を使います。保存する値には、あとで取り出すための属性名を付けます。
HttpSession session = request.getSession();session.setAttribute("属性名", 値);GreetServlet で、受け取った名前をセッションに保存します。request ではなくセッションに入れることで、この値は次のリクエストにも残ります。
@WebServlet("/greet")public class GreetServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { // 入力された名前を取得し、セッションに保存する String name = request.getParameter("name"); HttpSession session = request.getSession(); session.setAttribute("name", name);
response.setContentType("text/html; charset=UTF-8"); PrintWriter out = response.getWriter(); out.println("<p>こんにちは、" + name + "さん</p>"); out.println("<a href=\"/mypage\">マイページ</a>"); }}session.setAttribute("name", name) で、入力された名前を name という属性名でセッションに保存します。属性名の "name" は取り出すときの目印で、保存する値(利用者が入力した名前)とは別のものです。HttpSession は jakarta.servlet.http.HttpSession の import が加わります。あわせて、別のページ /mypage へのリンクを表示しています。
2-3. セッションからの取得
Section titled “2-3. セッションからの取得”保存した値は、別のサーブレットから getAttribute で取り出せます。マイページを表示する MyPageServlet で、セッションに保存された名前を取得します。import は GreetServlet と同じく jakarta.servlet 系と HttpSession、java.io の PrintWriter・IOException です。
Object 値 = session.getAttribute("属性名");@WebServlet("/mypage")public class MyPageServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { // セッションから名前を取り出す HttpSession session = request.getSession(); String name = (String) session.getAttribute("name");
response.setContentType("text/html; charset=UTF-8"); PrintWriter out = response.getWriter(); out.println("<p>" + name + "さんのマイページ</p>"); }}getAttribute の戻り値は Object 型です。セッションには文字列でも数値でも保存できるため、取り出すときの型は Object に統一されています。Object のままでは String のメソッドを呼べないので、保存したときの型に戻してから使います。
変数の前に (型) を付けて、指定した型として扱う記法を キャスト と呼びます。"name" には String の値を保存したので、(String) でキャストして受け取ります。保存したときと違う型でキャストすると、実行時にエラーになります。
/greet で名前を送信したあと、リンクから /mypage を開くと、別のサーブレットである MyPageServlet が、GreetServlet の保存した名前を取り出して表示します。
<p>田中さんのマイページ</p>/greet と /mypage は別々のリクエストですが、同じ利用者からのリクエストなので、セッションを通じて名前が引き継がれます。