HTML フォームで送信ボタンを押すと、ブラウザはフォームの内容を POST リクエストとしてサーバーに送ります。 このとき、フォームデータはリクエストボディにname=%E7%94%B0%E4%B8%AD&age=30のような URL エンコード形式で格納されます。サーバー側ではこれを正しく読み取り・デコードする必要があります。
このサンプルで学べること
Content-Length ヘッダーを使った POST ボディの正確な読み取り
URLDecoder を使った URL エンコード文字列のデコード
303 See Other によるリダイレクト(PRG パターン)の実装
ヘッダーを Map に格納して後から参照する方法
クラス / メソッド
役割
Content-Length ヘッダー
POST ボディのバイト数を示す。この値ぶんだけ読む
reader.read(buf, 0, length)
指定バイト数だけ読む(readLine() は使わない)
URLDecoder.decode(str, "UTF-8")
URL エンコードされた文字列を元に戻す
303 See Other
POST 送信後にリダイレクトして二重送信を防ぐ(PRG パターン)
Location ヘッダー
リダイレクト先のURLを指定する
サンプルコード
Java 8 版ではクラス・Map を使ったシンプルな実装です。 Java 17 版では record で フォームデータを型安全な不変クラスとして表現し、テキストブロックで HTML を見やすく記述します。 Java 21 版では Thread.ofVirtual() による Virtual Thread で同時接続を効率的に処理します。
POST ボディには改行が含まれないことが多く、 readLine() を使うとボディの終端まで待ち続けてハングアップします。 Content-Length ヘッダーで指定されたバイト数だけ読む reader.read(buf, 0, length) を使いましょう。
Sample.java
// NG: readLine() で POST ボディを読む(ハングアップの原因)
String body = reader.readLine(); // ← 改行が来るまで無限待ち
// OK: Content-Length ぶんだけ読む
String contentLength = headers.get("content-length");
if (contentLength != null) {
int length = Integer.parseInt(contentLength);
char[] buf = new char[length];
reader.read(buf, 0, length); // 指定バイト数だけ読む
body = new String(buf);
}
POST 後は 303 リダイレクトを使う(PRG パターン)
POST リクエストに対して直接 200 OK でHTMLを返すと、ブラウザのリロードや戻るボタンを押したときに 同じ POST が再送信されます(二重送信)。303 See Other でリダイレクトすることで ブラウザに GET リクエストを発行させ、二重送信を防止できます。これを PRG(Post-Redirect-Get)パターンといいます。
Sample.java
// NG: POST に対して 200 OK で HTML を返す(リロードで再送信される)
sendResponse(out, 200, "OK", "text/html", "<html>...</html>");
// OK: POST 後は 303 See Other でリダイレクトする
writer.print("HTTP/1.1 303 See Other\r\n");
writer.print("Location: /result\r\n"); // リダイレクト先
writer.print("Content-Length: 0\r\n");
writer.print("Connection: close\r\n");
writer.print("\r\n");