Angularでのルーティング設定とHTTPステータス404（Not Found）対策

お勉強したのでメモ書きです。
主にAngularでのHTTPステータス404の対策について記載しているので、興味があれば読んでみてください。

[参考(Angular公式サイト)](https://angular-jp.translate.goog/guide/deployment?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja&_x_tr_pto=nui,sc)
[参考のなったサイト](https://weblabo.oscasierra.net/apache-rewritecond-base/)
[参考のなったサイト2](https://www.webdesignleaves.com/pr/html/htaccess.html)

# ルーティング
ルーティング情報をRoutes配列で定義する
pathやredirectTo、ガード等を定義できる
ここではroutes配列でルーティングを定義している

```app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ExampleComponent } from './example/example.component';
import { MainComponent } from './main/main.component';
import { ErrorComponent } from './error/error.component';

const routes: Routes = [
  { path: 'exam', component: ExampleComponent },
  { path: '', component: MainComponent},
  { path: '**', component: ErrorComponent}  //その他は**で表す
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }
```
forRoot()メソッドでルーティング情報付きのRouterModuleモジュールを生成する
forRoot()メソッドはメインモジュールでのみ利用できる。
その他のモジュールでルーティング情報を追加する場合はforChild()メソッドを利用する
構文

```
forRoot(routes [,config])
```


ルート設定（引数configの主なパラメーター）

|パラメーター名|概要|
|-|-|
|enableTracing|ルーター内部で発生するイベント情報のログ出力をするか|
|**useHash**|ハッシュモード（「～/#/exam」のようなハッシュ（#～）でパスを表現するモード）を有効にするか|
|initialNavigation|初期アクセス時にトップページに遷移するか|
|errorHandler|例外発生時の処理を規定|


# useHashとNot Found
通常、「`http://localhost/`」を指定した場合、index.htmlを取得する。（URLは適宜自分の環境で読み替えてください）
ではapp-routing.module.tsで定義したURLを指定した場合どうなるか。
例えば、「`http://localhost/exam`」を指定した場合`http://localhost/exam 404 (Not Found)`がブラウザに表示されてAngularアプリケーションが読み込まれない。
これは`/exam `ディレクトリのindex.htmlをリクエストしてしまっていることが原因。`http://localhost/exam`と言うURLは`http://localhost/index.html`ブラウザに読み込まれた後、Angularが書き換えているだけなので、まずはどうにかして`http://localhost/index.html`をサーバーからブラウザに返さないといけない。
（apache等を使わずにng serveを使った場合は404が出力されず、Angularアプリケーションが読み込まれる。存在しないディレクトリにアクセスされたとき、index.htmlを返す設定が組み込まれているらしい。）  
`http://localhost/exam`等、ルーティングで定義したURLを直接指定した場合（ブラウザでリロードF5した場合等）でもindex.htmlを読み込むようにするためには、useHashを使う。
useHashを使うことでURLが`http://localhost/#/exam`のようにフラグメント演算子（#）を用いて表現される。  
フラグメント演算子より後ろの文字列はサーバー上では認識されないため無視されて、リロードした場合でも`http://localhost/`をリクエストした場合と同じ動作をする。
つまり、index.htmlが読み込まれる。

# useHashとブックマーク
ただし、useHashだけではブックマーク等もすべてトップページに遷移してしまう。
これを避けるためにはWEBサーバの設定を変更して、サーバ上に存在しないディレクトリをリクエストした時、**アドレスバーの値は変えずにindex.htmlを読み込む**設定を加える必要がある。そこで以下のように設定を加える。

例：apacheの場合

```httpd.conf
DocumentRoot "/Users/user/OneDrive/Documents/myDocuments/Angular/AngularApplicationProgramming/SampleApp/dist/SampleApp"
<Directory "/Users/user/OneDrive/Documents/myDocuments/Angular/AngularApplicationProgramming/SampleApp/dist/SampleApp">
	RewriteEngine On
	# If an existing asset or directory is requested go to it as it is
	RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
	RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
	RewriteRule ^ - [L]

	# If the requested resource doesn't exist, use index.html
	RewriteRule ^ /index.html

...
</Directory>

```

大事なのは以下の部分 
存在しないファイル・ディレクトリをリクエストされたとき、/ディレクトリ下(DocumentRoot)にあるindex.htmlを読み込むように設定している。


```httpd.conf
	RewriteEngine On
	# If an existing asset or directory is requested go to it as it is
	RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
	RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
	RewriteRule ^ - [L]

	# If the requested resource doesn't exist, use index.html
	RewriteRule ^ /index.html
```

apache初心者なりに頑張って意味を解説してみる。
まず、`#`から始まる行はコメント。
`RewriteEngine On`でリダイレクトを設定する機能を開放している。
`RewriteCond`には条件を記述して、条件に合うとき`RewriteRule`に従って動作をしている。
`RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f`の意味は「リクエストされたファイルが存在するときtrue」。
`RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d`は「リクエストされたディレクトリが存在するときtrue」という意味。
`[OR]`でつながっているため、リクエストされたファイルorディレクトリが存在するとき`RewriteRule ^ - [L]`すると記述されている。
`RewriteRule ^ - [L]`は「RewriteRule 正規表現 オプション」の順で記載されている。今回、正規表現はハットのみ（`^`）。ハットは先頭という意味なので任意の文字に当てはまる。
オプションは`- [L]`の部分。ハイフンは「オプションの操作だけを実行して、アドレスは置換しない」という意味。`[L]`はLastを意味し、条件に当てはまるアドレスを書き換えず処理を終了するという意味になる。
つまり、ファイル・ディレクトリが存在するときは何も書き換えられずレスポンスが返されることになる。
また、その次の一文でRewriteCondが指定されずにRewriteRuleが記載されている。つまり、それ以外は`RewriteRule ^ /index.html`すると記載されている。
`RewriteRule ^ /index.html`は任意の文字列を「/index.html」に書き換えるという意味。
これで存在しないファイル・ディレクトリが指定されたときはindex.htmlがレスポンスされるようになった。
試しにリロード（F5）をしたが、404にならずきちんとルーティングで定義したコンポーネントが表示された。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2130321/b217ab3e-b187-76a2-da57-e6898fb8c681.png)