サーブレットからJSPへ画面遷移
9.1 サーブレットからJSPへ画面遷移
これまで作成したサーブレットでは、フォームから受け取ったデータは、そのデータを受け取ったサーブレット自身が最終的な出力までを行なっていました。しかし、この構造は1.3.5項で解説したMVCモデルの観点から見ると一般的とは言えません。本来であれば、サーブレットでは受け取ったデータの処理のみを行い、最終的な画面の出力はJSPに任せるのが望ましい形です。このように複数のサーブレットやJSPを連携して使う場合、ディスパッチと呼ばれる処理を転送する仕組みがよく利用されます。
9.1.1 ディスパッチとは
Webブラウザからのリクエストによって実行されているJSPや、サーブレットから他のJSPやサーブレットにリクエストを転送することを、ディスパッチと呼びます。このディスパッチには、フォワード(forward)とインクルード(include)の2つの種類あります。フォワードとインクルードはどちらも似たような機能ですが、画面出力の処理が完了するタイミングに違いがあります。
・ フォワード
フォワードは画面出力の処理が完全に転送先に移るため、転送先の処理の実行後は画面出力は行えません。
受け取ったデータをサーブレットで処理し、画面の出力をJSPが行うといったような、転送先のプログラムに出力処理を全て任せる場合に利用されます。
図 9.1.1: フォワードの処理の流れ
・ インクルード
インクルードは転送先のプログラムを実行した後、転送元に戻って画面出力の処理を行います。
JSP内で画面の出力を行う際に、共通となる画面部品を呼び出すといったような、一部の出力を別のプログラムに任せる場合に利用されます。
図 9.1.2: インクルードの処理の流れ
9.1.2 フォワードを利用した処理の転送
画面の出力をJSPで行う場合、サーブレットからフォワードを利用して処理の転送を行います。フォワードを行う場合の書式は以下のようになります。
書式:フォワードで処理を転送
一番上の行の記述は必要なクラスのインポート文です。
2行目の記述は転送を行うための準備のようなもので、HttpServletRequestクラスのgetRequestDispatcher()メソッドを利用してRequestDispatcherインタフェースのオブジェクトを取得しています。
実際の転送処理は、一番下の行に記述されたRequestDispatcherインタフェースのforward()メソッドを利用します。
また、書式の「遷移先のパス」部分には以下の2種類のパスが利用可能です。
① 頭に「/」のついたコンテキストルートからの相対パス
② リクエストされた現在のURLからの相対パス
フォワード処理の別の書き方本項で紹介しているフォワードの書式では、RequestDispatcherクラスのオブジェクトを生成する記述とforward()メソッドの実行を2行に分けて記述していましたが、以下のようにgetRequestDispatcher()メソッドにforward()メソッドをつなげて1行で記述することも可能です。
request.getRequestDispatcher(“遷移先のパス”).forward(request, response);
それでは、実際にプログラムを作成し、使い方や注意点について学習していきましょう。
サーブレットからJSPへフォワードするプログラム
サーブレットからJSPへフォワードするプログラムを作成し、JSPの画面がWebブラウザに表示されることを確認してみましょう。
実行結果
アプリケーション構成
➢ UseForwardServlet1.java① ソース・フォルダ :web_basic/src/main/java
② パッケージ :ch09
③ 名前 :UseForwardServlet1
④ スーパークラス :jakarta.servlet.http.HttpServlet
⑤ アクセスURL :http://localhost:8080/web_basic/UseForwardServlet1
package ch09; import java.io.IOException; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @WebServlet("/UseForwardServlet1") public class UseForwardServlet1 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("サーブレットの実行"); //フォワード先の指定 RequestDispatcher dispatcher = request.getRequestDispatcher("/view/ch09/useForward.jsp"); //フォワードの実行 dispatcher.forward(request, response); System.out.println("サーブレットの終了"); } }
➢ useForward.jsp① 親フォルダの入力または選択 :web_basic/src/main/webapp/view/ch09
② ファイル名 :useForward.jsp
③ アクセスURL :UseForwardServlet1.javaからの画面遷移でアクセスされる
<%@page contentType= "text/html; charset=UTF-8" %> <% System.out.println("JSPの実行"); %> <html> <head> <title>フォワードを利用したプログラム</title> </head> <body> 転送されたJSPです。 </body> </html> <% System.out.println("JSPの終了"); %>
解説
このプログラムではUseForwardServlet1.javaファイルにアクセスしていますが、実際に表示された画面はuseForward.jspファイルで生成された画面になっています。このように、フォワードを利用することでサーブレットから処理を転送しJSPで画面の生成が行えます。
UseForwardServlet1.javaの15行目では、request.getRequestDispatcher()メソッドを利用してRequestDispatcherクラスのオブジェクトを取得しています。
このオブジェクトはgetRequestDispatcher()メソッドの引数に渡されたパスをもとに転送先を定義します。
パスには「/view/ch09/useForward.jsp」と定義しています。このパスは先頭に「/」が付いたコンテキストルート以下のパスとなっています。これにより、getRequestDispatcher()メソッド内でコンテキストパスからの相対パスが遷移先として指定されます。
次の21行目で処理の転送が行われます。ここでuseForward.jspに処理が移り、画面の生成が行われます。
このプログラムでは注意する点が2つあります。
まず1つ目はブラウザのアドレスバーに表示されているURLです。最終的に表示されている画面はJSPのものですが、アドレスバーに表示されるURLはサーブレットのURLになります。
図 9.1.3:アクセスURLとブラウザ表示の違い
ディスパッチでの転送処理はサーバ側で行われる処理なので、ブラウザ側から指定するURLは変わりません。そのため、JSPで相対パスのリンクを記述している場合は、JSPのファイルがある場所からの相対パスではなく、サーブレットのURLからの相対パスを指定しなければならない点に注意が必要です。
もう1つの注意点は、実際のプログラムの動きです。このプログラムの動きをわかりやすくするため、各所でコンソールへの出力を行っていますので、出力結果を確認してみましょう。すると次のように表示されているのが確認できます。
図 9.1.4:コンソール画面の出力結果
このプログラムの流れは実際には以下のようになっています。
図 9.1.5: フォワード時のプログラム処理の流れ
① UseForwardServletサーブレットにアクセスされます。
② 「サーブレットの実行」がコンソールに出力されます。
③ フォワードが実行されuseForward.jspに処理が転送されます。
④ 「JSPの実行」がコンソールに出力されます。
⑤ レスポンスを返しブラウザに画面が表示されます。
⑥ 「JSPの終了」がコンソールに出力されます。
⑦ useForward.jspの処理が終了しUseForwardServletサーブレットに戻ります。
⑧ 「サーブレットの終了」がコンソールに出力されます。
⑨ UseForwardServletサーブレットの処理が終了します。
フォワードは転送先で画面出力の処理を行いますが、転送先でプログラム自体が終了するわけではありません。通常のメソッドなどを利用した場合と同じように、呼び出し元に戻って処理が継続されます。
そのため、サーブレットの途中でforward()メソッドを記述するような場合は、forward()メソッドの後の処理の注意が必要です。
このプログラムではサーブレットからJSPに転送を行いましたが、フォワードは別のサーブレットに転送し、呼び出されたサーブレットからさらにJSPに転送するといった使い方もできます。
次に、「サーブレットA」→「サーブレットB」→「JSP」といった複数の転送を行うプログラムを見ていきましょう。
複数のフォワードを行うプログラム
サーブレットから別のサーブレットへ転送し、さらに転送後のサーブレットからJSPへ転送します。JSPの画面がWebブラウザに表示されることを確認しましょう。
なお、サーブレットからJSPへ転送するプログラムは、1つ前に作成したUseForwardServlet1.javaプログラムを利用し、ここではUseForwardServlet1.javaにフォワードするプログラムのみ作成します。
実行結果
アプリケーション構成
➢ UseForwardServlet2.java① ソース・フォルダ :web_basic/src/main/java
② パッケージ :ch09
③ 名前 :UseForwardServlet2
④ スーパークラス :jakarta.servlet.http.HttpServlet
⑤ アクセスURL :http://localhost:8080/web_basic/UseForwardServlet2
package ch09; import java.io.IOException; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @WebServlet("/UseForwardServlet2") public class UseForwardServlet2 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //フォワード先の指定 RequestDispatcher dispatcher = request.getRequestDispatcher("/UseForwardServlet1"); //フォワードの実行 dispatcher.forward(request, response); } }
解説
このプログラムではUseForwardServlet2.javaファイルにアクセスしていますが、実際に表示された画面はuseForward.jspファイルで生成された画面になっています。このプログラムの流れは次の図のようになっています。
図 9.1.6: 複数のフォワードを行うプログラム処理の流れ
このように、転送先のサーブレットでフォワードが利用されている場合は、さらに転送が行われ、そこで画面の生成が行われます。
9.1.3 転送先を動的に変える
前節までのプログラムでは、遷移先は必ず1つでした。しかしプログラムによっては、転送先を動的に変化させたい場合があります。例えば、フォームから入力された値が正しい場合は結果画面に転送し、不正な場合はエラーページに転送するというときなどです。
このような場合、if文などの条件文とフォワードを組み合わせて利用します。
では、早速プログラムを作成してみましょう。
状況によって転送先を変化させるプログラム
フォーム画面から数字を入力し、画面に表示するプログラムです。数字以外の文字が入力されたり未入力だった場合や、不正なアクセスの場合に、エラーメッセージを表示する画面に転送されます。
実行結果(正常の場合)
実行結果(文字が入力された場合)
実行結果(空白の場合)
実行結果(ダイレクトアクセスの場合)
アプリケーション構成
➢ changeForward1.jsp① 親フォルダの入力または選択 :web_basic/src/main/webapp/view/ch09
② ファイル名 :changeForward1.jsp
③ アクセスURL :http://localhost:8080/web_basic/view/ch09/changeForward1.jsp
1: <%@page contentType= "text/html; charset=UTF-8" %> 2: 3: <html> 4: <head> 5: <title>転送先を動的に変化させるプログラム</title> 6: </head> 7: <body> 8: <form action="<%= request.getContextPath() %>/ChangeForwardServlet"> 9: 数字を入力してください<br> 10: <input type="text" name="num"><br> 11: <input type="submit" value="送信"> 12: </form> 13: </body> 14: </html>
➢ ChangeForwardServlet.java① ソース・フォルダ :web_basic/src/main/java
② パッケージ :ch09
③ 名前 :ChangeForwardServlet
④ スーパークラス :jakarta.servlet.http.HttpServlet
⑤ アクセスURL :changeForward1.jspからの画面遷移でアクセスされる
package ch09; import java.io.IOException; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @WebServlet("/ChangeForwardServlet") public class ChangeForwardServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String strNum = request.getParameter("num"); //入力パラメータの取得 String message = null; //エラーメッセージ用変数 //入力パラメータのエラーチェック if(strNum == null){ message = "フォーム画面から入力してね。"; }else if(strNum.equals("")){ message = "何も入力されていませんよ。"; }else{ try{ Integer.parseInt(strNum); }catch(NumberFormatException e){ message = "数字を入力してね。"; } } //条件分岐による遷移先の変更処理 if(message == null){ RequestDispatcher dispatcher = request.getRequestDispatcher("/view/ch09/changeForward2.jsp?strNum="+strNum); dispatcher.forward(request, response); }else{ RequestDispatcher dispatcher = request.getRequestDispatcher("/view/ch09/changeForward3.jsp?message="+message); dispatcher.forward(request, response); } } }
➢ changeForward2.jsp① 親フォルダの入力または選択 :web_basic/src/main/webapp/view/ch09
② ファイル名 :changeForward2.jsp
③ アクセスURL :ChangeForwardServlet.javaからの画面遷移でアクセスされる
<%@page contentType= "text/html; charset=UTF-8" %> <% String strNum = request.getParameter("strNum"); %> <html> <head> <title>転送先を動的に変化させるプログラム</title> </head> <body> 入力された数字は「<%= strNum %>」です。 </body> </html>
➢ changeForward3.jsp① 親フォルダの入力または選択 :web_basic/src/main/webapp/view/ch09
② ファイル名 :changeForward3.jsp
③ アクセスURL :ChangeForwardServlet.javaからの画面遷移でアクセスされる
<%@page contentType= "text/html; charset=UTF-8" %> <% String message = request.getParameter("message"); %> <html> <head> <title>転送先を動的に変化させるプログラム</title> </head> <body> エラーです。<br> <%= message %> </body> </html>
解説
このプログラムは、以下の4つのファイルで構成されています。
① 入力フォーム :changeForward1.jsp
② 処理を行うサーブレット :ChangeForwardServlet.java
③ 入力された値を表示する画面 :changeForward2.jsp
④ エラーメッセージを表示する画面 :changeForward3.jsp
このプログラムで重要な点は、ChangeForwardServletサーブレットの35行目から43行目の記述です。
ここでは条件式によって、処理の転送先を2つに分けています。入力された値に問題がない場合は35行目のifブロックが実行され、changeForward2.jspに転送されます。また問題があった場合は、39行目からのelseブロックが実行されchangeForward3.jspに転送されます。
このように、条件分岐を使うことでフォワード先を動的に変更することができます。ただし、サーブレットの処理内でフォワードが2回実行されるような状態になると遷移先が各条件に応じて変わらず先に書かれた遷移先へ転送されるので、複数のフォワードを記述する際には注意が必要です。
また、このプログラムではフォワードする際に指定するパスの後ろに直接パラメータを記述しています。これは、if文の遷移先であるchangeForward2.jspで参照しているnumという名前のパラメータは、changeForward1.jspの入力フォームで既に設定されている名前なので、改めて渡し直さなくても遷移先で使うことができます。一方、else文の遷移先であるchangeForward3.jspで参照しているmessageという名前のパラメータは、このChangeForwardServletで初めて設定するパラメータになります。このような場合は、画面遷移するタイミングで、URLに埋め込む形でパラメータを追加で渡す必要があります。
フォワードではこのような値の渡し方も可能なことも覚えておきましょう。