JavaのWebアプリで値が渡らないのはなぜ?Servlet・Model・JSPの確認方法

フォームには入力したのに、Servletでは値を取得できない。Servletで用意した結果が、JSPではnullになる。こうしたとき、まず確認したいのは「値を渡すときの名前」と「取り出すときの名前」です。
Webアプリでは、別のファイルに書かれた処理が、名前を使ってデータを受け渡します。フォームがquantityという名前で送った値を、Servletがcountという名前で探しても取得できません。大切なのは、すべての名前を同じにすることではなく、どの名前とどの名前を対応させるのかを理解することです。
本記事は、Schoo「Java入門 中級」第6回「Webアプリケーション構築演習(基礎編)」を学んだあとに理解を深めるステップアップ学習記事です。未受講の方も、掲載した入力例・コード・結果を読み比べて理解できる構成です。コードは説明に必要な部分を抜粋しており、授業の配布ファイルや実行環境を用意する必要はありません。
名前は、別々のファイルの処理をつなぐ約束
ブラウザやJavaが、「これは数量だから、countでもquantityでも同じだろう」と判断してくれるわけではありません。指定された名前を使って値を探します。意味が似ていても、綴りが違えば別の名前です。
HTMLとServletで揃える名前:quantity
- HTMLの送信項目:
name="quantity" - Servletで探す名前:
getParameter("quantity")
ServletとJSPで揃える名前:quote
- Servletで保存する名前:
setAttribute("quote", result) - JSPで探す名前:
getAttribute("quote")
この二組を、それぞれ見比べます。quantityとQuantityのような大文字・小文字の違いにも注意してください。名前が文字列として書かれているため、両側が食い違っていてもJavaのコンパイルだけでは気付けないことがあります。
同じにする名前と、同じでなくてもよい名前
HTMLからServletへ:nameとgetParameterの引数を揃える
<input id="quantityField" name="quantity" type="text">
String text = request.getParameter("quantity");
ここで対応するのは、二つの"quantity"です。id="quantityField"はHTML内で要素を識別するための指定、textは取り出した値を受け取るJavaの変数名です。idや変数名をquantityに揃えなくても取得できます。
ServletからJSPへ:setAttributeとgetAttributeの属性名を揃える
request.setAttribute("quote", result);
QuoteResult displayResult = (QuoteResult) request.getAttribute("quote");
こちらで対応するのは、二つの"quote"です。Servletの変数名がresult、JSPの変数名がdisplayResultでも、同じリクエストのquote属性からオブジェクトを取り出せます。変数名を同じにしたからデータが届くのではなく、属性名を指定して取り出しているのです。
JSPの変数名をdisplayResultにする場合、そのJSPで値を表示する箇所もdisplayResult.getTotal()のように揃えます。以降の例では、JSP側の変数名もresultとします。
なお、quantityは入力の項目名、quoteは計算結果の属性名です。別の受け渡しなので、この二つまで同じ名前にする必要はありません。
確認に使う例:数量5を送ると合計2800円になる
- 単価は500円。数量は1〜20の整数を受け付ける。
- 小計は単価×数量。小計3000円未満なら送料300円、3000円以上なら送料0円。
- 合計は小計+送料。DBへの保存は行わない。
| 確認する値 | 数量5の期待値 |
|---|---|
| 送信する項目 | quantity=5 |
| Servletが受け取る文字列 | “5” |
| 整数へ変換した数量 | 5 |
| 小計 | 2500円 |
| 送料 | 300円 |
| 合計 | 2800円 |
この例に登場するQuoteModelは送料と合計を計算するクラス、QuoteResultは計算結果を持つDTOです。QuoteResultには小計・送料・合計のフィールドがあり、getSubtotal・getShippingFee・getTotalでそれぞれの値を取り出せるものとします。授業のファイル名を知っている必要はありません。
正常な受け渡しの例:QuoteServlet.javaの処理の抜粋
String text = request.getParameter("quantity");
int quantity = Integer.parseInt(text);
QuoteModel model = new QuoteModel();
QuoteResult result = model.calculate(quantity);
request.setAttribute("quote", result);
request.getRequestDispatcher("/quote-result.jsp").forward(request, response);
ここでは有効な数量5が送られた場合に絞っています。文字列”5″を整数5へ変換し、Modelの計算結果をquote属性でJSPへ渡します。実際の入力チェックは、HTML・Servlet・Modelの入力チェックの記事で扱っています。
画面に2500円と出ても、それだけでは「送料を加え忘れた」のか「合計欄に小計を表示した」のか分かりません。同じ症状でも原因は複数あります。コードをあちこち変える前に、どこまで期待値と一致しているかを確かめます。
イラスト:名前の対応を、データの流れの中で確認する

図では、送信するquantity=5と①のgetParameter("quantity")、③のsetAttribute("quote", result)と④のgetAttribute("quote")を見比べてください。データが受け渡されるところで、どの名前が対応しているかがポイントです。②はModelで計算する処理です。
ケース1:nameとgetParameterの名前が違う
入力欄には5を入れたのに、Servletでtextがnullになっていました。次の組み合わせを見てください。
HTMLはquantityという名前で送信
<input name="quantity" type="text">
間違い:Servletはcountという名前で取得
String text = request.getParameter("count");
画面に書かれた「数量」というラベルやidではなく、nameの値が送信する項目の名前になります。quantityとして送った値をcountで探しても取得できず、textはnullになります。入力した5が空になったのではなく、違う名前の項目を探しています。
修正:取得する名前をquantityに揃える
String text = request.getParameter("quantity");
quantity=5が送信されている条件なら、修正後に取得するtextは文字列”5″です。自分のコードで名前を揃えてもnullになる場合は、送信元のフォーム・nameの有無・disabledの指定も確認します。名前が合っているだけで必ず送信されるわけではありません。
| 状態 | 意味 | まず見る場所 |
|---|---|---|
| textがnull | その名前のパラメータがない | HTMLのnameとgetParameterの引数、実際の送信内容 |
| textが空文字 | パラメータはあるが値が空 | 入力欄と送信した値 |
| textが”5″ | 今回の受け取りは期待どおり | 整数へ変換した後とModelの呼び出し |
ケース2:setAttributeとgetAttributeの名前が違う
Servletの出力にはtotal=2800があるのに、JSPではquote is null=trueでした。
Servlet:quoteという属性名で保存
request.setAttribute("quote", result);
間違い:JSPではresultという属性名を探している
QuoteResult result = (QuoteResult) request.getAttribute("result");
Javaの変数名resultと、属性名”quote”は別です。setAttributeの第1引数が受け渡し用の名前、第2引数が渡すオブジェクトです。JSPの変数名をresultにしても、属性名が自動でresultになるわけではありません。
修正:getAttributeでもquoteを指定
QuoteResult result = (QuoteResult) request.getAttribute("quote");
属性名が違えばresultはnullです。nullを確認して案内文を出すJSPもあれば、確認せずresult.getTotal()を呼んでNullPointerExceptionになるコードもあります。取得できない原因と、そのあと画面に何が出るかは分けて考えます。
名前が一致している場合は、setAttributeの行を通っているか、JSPへforwardしているか、結果JSPを直接開いていないかを確認します。リダイレクト後の新しいリクエストには、元のリクエスト属性はそのまま残りません。
ケース3:表示したい項目とgetterの名前が合っていない
Servletの合計も、JSPで確認したresult.getTotal()も2800です。それでも画面の合計欄が2500円なら、次に見るのはその欄の表示コードです。
間違い:合計欄で小計のgetterを呼んでいる
<p>合計: <%= result.getSubtotal() %>円</p>
修正:合計のgetterを使う
<p>合計: <%= result.getTotal() %>円</p>
getSubtotalは小計、getShippingFeeは送料、getTotalは合計を返します。表示が間違っていてもDTOの値は正しいため、ここでModelの計算式を変えると、別の場所まで壊してしまいます。
何の名前か分かるように付け、変更するときは対応先も確認する
名前は一致していれば何でも読みやすい、というわけではありません。quantityなら数量、shippingFeeなら送料、totalなら合計だと読み取れます。用途の違う値をすべてdataやvalueと呼ぶより、何を受け取り、何を表示しているのか分かる名前にすると、間違いを見つけやすくなります。
たとえばフォームのnameをquantityからcountへ変更するなら、ServletのgetParameterもcountへ変更します。quote属性を別名にするなら、setAttributeとgetAttributeの両方を確認します。一方、Javaのローカル変数を改名する場合は、その変数を使っているコードを揃えます。変数の改名と、受け渡しに使う文字列の変更は別の作業です。
途中の値を出力すると、名前の間違いをどう見つけられる?
System.out.println()で途中の値を出力すると、どこまでデータが届いているかを調べられます。ここでは確認するコードと出力例を並べるので、実行環境がなくても読み比べられます。
出力先はブラウザのConsoleではなく、Javaを実行しているTomcat側のコンソールです。自分の学習用アプリを調べる場合は、一時的に確認行を入れて使えます。実際の顧客情報・パスワードなどは出力せず、調査が終わったら確認用の行を外します。
Servlet:値を取得した直後
String text = request.getParameter("quantity");
System.out.println("Servlet text=[" + text + "]");
角括弧は確認用の目印です。[5]なら文字列5、[]なら空文字です。[null]ならパラメータが存在しない可能性があります。ただし文字列としてnullを送っても同じ表示になるため、必要ならtext == nullも確認します。
Servlet:Modelを呼ぶ直前と、結果が返った直後
System.out.println("Servlet quantity=" + quantity);
result = model.calculate(quantity);
System.out.println("Servlet subtotal=" + result.getSubtotal());
System.out.println("Servlet shippingFee=" + result.getShippingFee());
System.out.println("Servlet total=" + result.getTotal());
JSP:属性を取り出した直後
<%
QuoteResult result = (QuoteResult) request.getAttribute("quote");
System.out.println("JSP quote is null=" + (result == null));
if (result != null) {
System.out.println("JSP total=" + result.getTotal());
}
%>
JSPのこの部分もサーバー側のJavaとして実行されるため、出力先はTomcat側です。result == nullがtrueなら、ここではDTOを取得できていません。nullのままgetterを呼ばないよう、確認してから値を読みます。
数量5を送ったときの正常な出力
Servlet text=[5] Servlet quantity=5 Servlet subtotal=2500 Servlet shippingFee=300 Servlet total=2800 JSP quote is null=false JSP total=2800
ServletやJSPという文字を付けているのは、どこで確認した値かを見分けるためです。図の①で受け取った値、②の計算を呼ぶ前後、③を経て④で取り出した値の順に照合します。
名前が合っている場合は、実際に渡す値も確認する
Servletでは文字列5を受け取り、整数のquantityも5でした。しかしModelから返った小計は500円、合計は800円です。
System.out.println("Servlet quantity=" + quantity);
result = model.calculate(1);
変数quantityを表示していても、実際の引数が1なら、Modelには1が渡ります。「quantityは5だった」で確認を終えず、呼び出す行そのものを見ます。
修正:受け取った数量を引数にする
result = model.calculate(quantity);
quantityを渡せば、数量5なら合計2800円、数量6なら合計3000円というように、入力に応じた結果になります。calculate(5)に直すだけでは、別の数量を送っても5として計算するため、修正にはなりません。
引数が正しいのに結果が違う場合は、Modelの中を見る
int subtotal = 500 * quantity;
int shippingFee = 300;
if (subtotal >= 3000) {
shippingFee = 0;
}
int total = subtotal; // 間違い:送料を足していない
この場合、数量5の小計2500円と送料300円は正しくても、合計が2500円です。修正箇所はJSPではなく、int total = subtotal + shippingFee;の行です。DTOへ渡すコンストラクタの引数も、new QuoteResult(subtotal, shippingFee, total)の順になっているか確認します。
症状ではなく、途中の値を根拠に修正箇所を決める
| 数量5を送ったときの観察 | 次に調べる場所 |
|---|---|
| Servletでtextがnull | フォームの送信項目とgetParameter |
| quantityは5なのにModelの小計が500 | calculateに実際に渡した引数 |
| 小計2500・送料300なのにDTOの合計2500 | Modelの加算、DTOのコンストラクタとgetter |
| Servletの合計2800、JSPのresultはnull | 属性名・設定の有無・forward・開いたURL |
| JSPのgetTotalは2800、画面の合計2500 | 合計欄が使うgetterと、表示しているJSP |
これは今回の例で調べる順番であり、症状だけで原因を断定する表ではありません。正常な値が確認できた場所と、違いが確認できた場所の間を調べることで、確認するコードを狭められます。
読み比べる練習:どこで値が変わったかを追う
- ケース1〜3から一つを選び、渡す側と取り出す側の名前を紙などに並べる。
- 本文の取得値や表示結果を見て、どこまでは期待どおりだったかを示す。
- 直す箇所を一つ決め、その修正で結果がどう変わるか説明する。
- 数量が5ではなく6だった場合も、正しい結果になる修正か考える。
| 入力 | 小計 | 送料 | 合計 |
|---|---|---|---|
| 1 | 500 | 300 | 800 |
| 5 | 2500 | 300 | 2800 |
| 6 | 3000 | 0 | 3000 |
| 20 | 10000 | 0 | 10000 |
数量6は送料が0円になる境目です。上の仕様を使って小計・送料・合計を計算し、表と比べられます。数量5の結果だけを固定して返す修正では、他の数量で正しい値にならないことが分かります。
確認問題
数量5で小計2500円・送料300円・合計2800円になる仕様です。特に「どの名前同士を対応させるか」に注目して答えてください。
- HTMLは
id="quantityField" name="quantity"です。ServletではgetParameter("count")を使い、値を取得できませんでした。どの名前に合わせて直しますか。idに合わせない理由も答えてください。 - Servletは
setAttribute("quote", result)で結果を保存しています。JSPのQuoteResult displayResult = (QuoteResult) request.getAttribute("quote");は、変数名が違っても取得できますか。同じリクエストでforwardしているものとして、理由を答えてください。 - Servletは
setAttribute("quote", result)を実行し、同じリクエストでJSPへforwardしています。JSPはgetAttribute("result")で取得してnullになりました。修正する名前を答えてください。また、保存する属性名をorderQuoteに変更するなら、どこを変更しますか。 - JSPの確認出力は
total=2800ですが、画面の合計欄には2500円と出ます。合計欄のコードがgetSubtotal()を呼んでいる場合、どのメソッド名へ直しますか。理由も答えてください。
演習後の確認:こちらのページを使って、自分で考えた内容を見直せます。
まとめ
- データの受け渡しでは、渡す側と取り出す側の名前を対応させる。
- HTMLのnameとgetParameterの引数、setAttributeとgetAttributeの属性名をそれぞれ揃える。
- HTMLのid・Javaの変数名・受け渡し用の名前を混同しない。すべてを同じ名前にする必要はない。
- 値の意味が分かる名前を付け、名前を変更するときは対応先も確認する。
- 名前の対応を確認したうえで、実際に取得した値と表示結果を確かめる。