1回でドンピシャの相対パスを書けるようになる！（RequestDispatcher　foward）

# 0_結論
最初に結論。

RequestDispatcherのfowardメソッドで遷移先のファイルを指定するときの書き方は、以下の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
```

```java:SampleServlet.java
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_階層について整理

まず、階層について整理します。

今回のフォルダ&ファイル構成で、今回注目すべき部分のみを図にまとめると、
こうなります。

```mermaid:

flowchart

%%---グループの設定---

%%第4階層のグループ
subgraph G4[第4階層]
  G4a[SampleServlet.java（★）]
  G4b[sample.jsp（★）]
end

%%第3階層のグループ
subgraph G3[第3階層]
  G3a{{samplePkg}}
  G3b{{jsp}}
end

%%第2階層のグループ
subgraph G2[第2階層]
  G2a{{src}}
  G2b{{SampleContent}}
end

%%第1階層のグループ
subgraph G1[第1階層]
  G1a{{SampleProject}}
end

%%---スタイルの設定---

%%第〇階層のスタイル（フォルダ>ルート）
classDef FOLr fill:#aaa,color:#fff,stroke:#fff
class G1a FOLr

%%第〇階層のスタイル（フォルダ>src）
classDef FOLsrc fill:#e83,color:#fff,stroke:#fff
class G2a,G3a FOLsrc

%%第〇階層のスタイル（ファイル>src配下）
classDef FILsrc fill:#e83,color:#fff,stroke:none
class G4a FILsrc

%%第〇階層のスタイル（フォルダ>SampleContent配下）
classDef FOLsmp fill:#006100,color:#fff,stroke:#fff
class G2b,G3b FOLsmp

%%第〇階層のスタイル（ファイル>SampleContent）
classDef FILsmp fill:#006100,color:#fff,stroke:none
class G4b FILsmp

%%グループのスタイル
classDef SG fill:none,color:#666,stroke:#aaa
class  G1,G2,G3,G4 SG
```
「今いる位置」と「遷移したい先」は、どちらも第4階層にあります。

今いる位置：SampleServlet.java　
…①SampleProject > ②src > ③samplePkg > ④SampleServlet.java（★）

遷移したい先：sample.jsp
…②SampleProject > ②SampleContent > ③jsp > ④sample.jsp（★）

※〇数字は階層

### 余談：Mermaid
今回、[Mermaid](https://qiita.com/Qiita/items/c07f3262d8f3b25f06c9)というものがあると知り、上図をMermaidで書いてみました。(↓こちらを参照して書きました）

https://qiita.com/b-mente/items/b17275090176d63d1d69

図形の形変えたいとか、線で繋ぎたいとか、高さを揃えたいとか…は追々使えるようになりたいです。

## 3-2_書き方_失敗：道順を全部書く

さて、相対パスを書きます。

現在地（スタート）は　④SampleServlet.java（★）で、
遷移したい先(ゴール） ④sample.jsp（★）です。

まずはスタートの第4階層から一度、スタートとゴールの共通の元となる第1階層（①SampleProjsct）まで戻って、
それからゴールの第4階層へ向かう、
という道順を考えてみます。

これでどうだ！
`/SampleProject/SampleContent/jsp/sample.jsp`
（これは絶対パスなのか？相対パスなのか？？）
実行してみます。

```java:SampleServlet.java
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);
		}
}
```
結果は、404エラーでした。失敗。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/80318a87-1f64-ec71-8093-58beceaad02f.png)

最初のスラッシュを無くして
`SampleProject/SampleContent/jsp/sample.jsp`
で実行しても、同じく404エラーでした。

「../」を使って
`../../SampleContent/jsp/sample.jsp`
で実行したら、500エラー。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/20f2e268-efde-e17b-5dba-138640437e4b.png)

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へ画面遷移」](https://kanda-it-school-kensyu.com/java-jsp-servlet-contents/jjs_ch09/jjs_0901/)神田ITScool)

* 書き方１：コンテキストルートからの相対パス
・　/（スラッシュ）で始まる。

* 書き方２：現在の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回でドンピシャの相対パスを書けるようになる！」
>
>パターン暗記ではなく、相対パスの通し方を理解して、どんな場合でも
「通るパスを自力で書けるようになる！」

まだパスの通し方について、理解できてません。
理解できたら追記します！

# 参考

https://www.ycomps.co.jp/staffblog/16101#:~:text=%E7%B5%B6%E5%AF%BE%E3%83%91%E3%82%B9%E3%81%AF%E3%80%81%E3%80%8C%E3%81%A9%E3%81%AE%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3,%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%81%8C%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82

https://kanda-it-school-kensyu.com/java-jsp-servlet-contents/jjs_ch09/jjs_0901/

https://kanda-it-school-kensyu.com/java-jsp-servlet-contents/jjs_ch09/jjs_0901/