0_結論
最初に結論。
RequestDispatcherのfowardメソッドで遷移先のファイルを指定するときの書き方は、以下の2通り。
-
書き方1:コンテキストルートからの相対パス
・ /(スラッシュ)で始まる。 -
書き方2:現在のURL(リクエストされたアドレスのパス)からの相対パス
・ / で始まらない。
本記事のサンプルで言うと、正解は
/jsp/sample.jsp
jsp/sample.jsp
(最初の「/」の有無は問わず実行成功するし、手元のテキストにも、「/」があるパターンと無いパターンの両方がお手本として掲載されている。)
1_何が起きたか
JavaのWebアプリ開発を学習中です。
サーブレット内にRequestDispatcherで遷移先のjspの相対パスを書くときに、
「現在書いているサーブレットjavaファイルを起点として、遷移先のjspファイルまでの道順を全部書けば良いと思っているのに、お手本のコードを見ると書かれているのは道順のラスト部分だけ。書くべきラスト部分の範囲がわからない!」
「最初の "/" は必要なのか不要なのか?」
という2点がわからなくなることが多いです。
相対パスの理解が浅いのだと思います。
よく迷うのが下記のようなケース。
SampleProject(コンテキストルート)
∟.settings
∟build
∟src
∟samplePkg(パッケージ)
∟SampleServlet.java(★)
∟SampleContent
∟META-INF
∟jsp
∟sample.jsp(★)
∟WEB-INF
∟lib
∟mysql-connector-j-9.1.0.jar
∟web.xml
∟.classpath
∟.project
package samplePkg;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SampleServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
doPost(req,res);
}
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
res.setContentType("text/html;charset=UTF-8");
//↓この"jsp/sample.jsp"部分!
RequestDispatcher rd = req.getRequestDispatcher("jsp/sample.jsp");
rd.forward(req, res);
}
}
現在のところは、
・書いたパスが正解なのか不正解なのか、実際にそのファイルをサーバーで実行して試してみる。→パスが通ってなければパスを修正する。
あるいは、
・「このときはこの書き方!」と、パターンを暗記して対応する。
という方法を取っています。
2_この記事のゴール
ファイルを実行して試さなくても、
「1回でドンピシャの相対パスを書けるようになる!」
パターン暗記ではなく、相対パスの通し方を理解して、どんな場合でも
「通るパスを自力で書けるようになる!」
3_相対パスの書き方
3-1_階層について整理
まず、階層について整理します。
今回のフォルダ&ファイル構成で、今回注目すべき部分のみを図にまとめると、
こうなります。
「今いる位置」と「遷移したい先」は、どちらも第4階層にあります。
今いる位置:SampleServlet.java
…①SampleProject > ②src > ③samplePkg > ④SampleServlet.java(★)
遷移したい先:sample.jsp
…②SampleProject > ②SampleContent > ③jsp > ④sample.jsp(★)
※〇数字は階層
余談:Mermaid
今回、Mermaidというものがあると知り、上図をMermaidで書いてみました。(↓こちらを参照して書きました)
図形の形変えたいとか、線で繋ぎたいとか、高さを揃えたいとか…は追々使えるようになりたいです。
3-2_書き方_失敗:道順を全部書く
さて、相対パスを書きます。
現在地(スタート)は ④SampleServlet.java(★)で、
遷移したい先(ゴール) ④sample.jsp(★)です。
まずはスタートの第4階層から一度、スタートとゴールの共通の元となる第1階層(①SampleProjsct)まで戻って、
それからゴールの第4階層へ向かう、
という道順を考えてみます。
これでどうだ!
/SampleProject/SampleContent/jsp/sample.jsp
(これは絶対パスなのか?相対パスなのか??)
実行してみます。
package samplePkg;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SampleServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
doPost(req,res);
}
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
res.setContentType("text/html;charset=UTF-8");
//↓ここ!
RequestDispatcher rd = req.getRequestDispatcher("/SampleProject/SampleContent/jsp/sample.jsp");
rd.forward(req, res);
}
}
最初のスラッシュを無くして
SampleProject/SampleContent/jsp/sample.jsp
で実行しても、同じく404エラーでした。
「../」を使って
../../SampleContent/jsp/sample.jsp
で実行したら、500エラー。
ChatGPTに聞いてみました。
(to ChatGPT)
"SampleProject/SampleContent/jsp/sample.jsp"の部分を
”jsp/sample.jsp”に変更すると正常に動作します。"SampleProject/SampleContent/jsp/sample.jsp"で実行すると、404エラーになります。
"../../SampleContent/jsp/sample.jsp"に変更すると、500エラーになります。なぜ、
"SampleProject/SampleContent/jsp/sample.jsp"、
"../../SampleContent/jsp/sample.jsp"
は不可なのでしょうか?
また、”jsp/sample.jsp”のほかにも、正常に動作する絶対パス又は相対パスは存在しますか?
3-3_書き方_正解
・/jsp/sample.jsp
・jsp/sample.jsp
→最初の「/」の有無は問わず実行成功するし、手元のテキストにも、
「/」があるパターンと無いパターンの両方がお手本として掲載されている。
ChatGPTの回答と、ネット&手持ちの教材で調べたことを加味して、以下のとおり理解しました。
書式の「遷移先のパス」部分には以下の2種類のパスが利用可能です。
① 頭に「/」のついたコンテキストルートからの相対パス
② リクエストされた現在のURLからの相対パス
(出典:「サーブレットからJSPへ画面遷移」神田ITScool)
-
書き方1:コンテキストルートからの相対パス
・ /(スラッシュ)で始まる。 -
書き方2:現在のURL(リクエストされたアドレスのパス)からの相対パス
・ / で始まらない。
因みに、jsp/sample.jsp
でも正常に実行できたけど、これはEclipseが自動で動くように解釈してくれたということなのか?あるいは、手元のテキストにも各々のプログラムで両方のパターンの記述がされているから、大本のルールも「どちらでもOK」ということなのか??
3-4_書き方_不正解
・/SampleProject/SampleContent/jsp/sample.jsp
→404エラー
・SampleProject/SampleContent/jsp/sample.jsp
(最初のスラッシュを無くした)
→404エラー
→リクエストディスパッチャーが探すパスが「コンテキスト・ルートからの相対パス」であるため、404エラーが発生している。「SampleProject」はサーブレットコンテナ(例: Tomcat)によってマッピングされた コンテキスト・ルート の名前であり、RequestDispatcher 内で明示的に書く必要は無い。
・../../SampleContent/jsp/sample.jsp
(「../」を使った)
→500エラー
→../../ のような親フォルダを参照する相対パスは、RequestDispatcher では解釈できない。サーブレットが動作するフォルダ(/SampleProject/)の外側を参照しようとするため、この指定は無効。
・sample.jsp
→404エラー。
→「書き方2」のとおり、/ で始まらない書き方は、「現在のURL(リクエストされたアドレスのパス)からの相対パス」。今回の遷移先のファイルは異なるディレクトリに存在するので、この書き方はエラーになる。
/SampleContent/jsp/sample.jsp
→404エラー。
→「書き方1」のとおりに記述したが、これだとエラー。書き方が間違っているのか?なぜエラーになるのか、「RequestDispatcher 相対パス」等でググるも解決できず。
なぜ/SampleContent
の記述が不要なのか、謎のまま…。
/SampleContent
をすっ飛ばして、/jsp/sample.jsp
が正解となる理由がわからない…。
4_解決できなかった点
- "3-3_書き方_正解" より
因みに、
jsp/sample.jsp
でも正常に実行できたけど、これはEclipseが自動で動くように解釈してくれたということなのか?あるいは、手元のテキストにも各々のプログラムで両方のパターンの記述がされているから、大本のルールも「どちらでもOK」ということなのか??
- "3-4_書き方_不正解" より
/SampleContent/jsp/sample.jsp
→404エラー。
→「書き方1」のとおりに記述したが、これだとエラー。書き方が間違っているのか?なぜエラーになるのか、「RequestDispatcher 相対パス」等でググるも解決できず。
なぜ/SampleContent
の記述が不要なのか、謎のまま…。
/SampleContent
をすっ飛ばして、/jsp/sample.jsp
が正解となる理由がわからない…。
5_さいごに
上記2点が未解決なので、この記事のゴール(下記)は達成できず。
ファイルを実行して試さなくても、
「1回でドンピシャの相対パスを書けるようになる!」パターン暗記ではなく、相対パスの通し方を理解して、どんな場合でも
「通るパスを自力で書けるようになる!」
まだパスの通し方について、理解できてません。
理解できたら追記します!
参考